1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import { 16 AddNoteArgs, 17 AddSpanNoteArgs, 18 Note, 19 NoteManager, 20 SpanNote, 21} from '../public/note'; 22import {randomColor} from '../components/colorizer'; 23import {raf} from './raf_scheduler'; 24 25export class NoteManagerImpl implements NoteManager { 26 private _lastNodeId = 0; 27 private _notes = new Map<string, Note | SpanNote>(); 28 29 // This function is wired up to clear the SelectionManager state if the 30 // current selection is a note. 31 // TODO(primiano): figure out some better (de-)coupling here. 32 // We cannot pass SelectionManager in our constructor because doing so would 33 // create a cyclic ctor dependency (SelectionManager requires NoteManager in 34 // its ctor). There is a 2way logical dependency between NoteManager and 35 // SelectionManager: 36 // 1. SM needs NM to handle SM.findTimeRangeOfSelection(), for [M]ark. 37 // 2. NM needs SM to tell it that a note has been delete and should be 38 // deselected if it was currently selected. 39 onNoteDeleted?: (nodeId: string) => void; 40 41 get notes(): ReadonlyMap<string, Note | SpanNote> { 42 return this._notes; 43 } 44 45 getNote(id: string): Note | SpanNote | undefined { 46 return this._notes.get(id); 47 } 48 49 addNote(args: AddNoteArgs): string { 50 const id = args.id ?? `note_${++this._lastNodeId}`; 51 this._notes.set(id, { 52 ...args, 53 noteType: 'DEFAULT', 54 id, 55 color: args.color ?? randomColor(), 56 text: args.text ?? '', 57 }); 58 raf.scheduleFullRedraw(); 59 return id; 60 } 61 62 addSpanNote(args: AddSpanNoteArgs): string { 63 const id = args.id ?? `note_${++this._lastNodeId}`; 64 this._notes.set(id, { 65 ...args, 66 noteType: 'SPAN', 67 id, 68 color: args.color ?? randomColor(), 69 text: args.text ?? '', 70 }); 71 raf.scheduleFullRedraw(); 72 return id; 73 } 74 75 changeNote(id: string, args: {color?: string; text?: string}) { 76 const note = this._notes.get(id); 77 if (note === undefined) return; 78 79 this._notes.set(id, { 80 ...note, 81 color: args.color ?? note.color, 82 text: args.text ?? note.text, 83 }); 84 raf.scheduleFullRedraw(); 85 } 86 87 removeNote(id: string) { 88 raf.scheduleFullRedraw(); 89 this._notes.delete(id); 90 this.onNoteDeleted?.(id); 91 } 92} 93