2021-03-14 18:39:08 +00:00
import * as loader from "tc-loader" ;
2023-11-21 00:58:05 +00:00
import { Stage } from "tc-loader" ;
import { ignorePromise , WritableKeys } from "tc-shared/proto" ;
import { LogCategory , logDebug , logError , logInfo , logTrace , logWarn } from "tc-shared/log" ;
import { guid } from "tc-shared/crypto/uid" ;
import { Registry } from "tc-events" ;
import { server_connections } from "tc-shared/ConnectionManager" ;
import { defaultConnectProfile , findConnectProfile } from "tc-shared/profiles/ConnectionProfile" ;
import { ConnectionState } from "tc-shared/ConnectionHandler" ;
2021-03-14 18:39:08 +00:00
import * as _ from "lodash" ;
2023-11-21 00:58:05 +00:00
import { getStorageAdapter } from "tc-shared/StorageAdapter" ;
2021-03-14 18:39:08 +00:00
type BookmarkBase = {
readonly uniqueId : string ,
displayName : string ,
previousEntry : string | undefined ,
parentEntry : string | undefined ,
} ;
export type BookmarkInfo = BookmarkBase & {
readonly type : "entry" ,
connectOnStartup : boolean ,
connectProfile : string ,
serverAddress : string ,
serverPasswordHash : string | undefined ,
defaultChannel : string | undefined ,
defaultChannelPasswordHash : string | undefined ,
}
export type BookmarkDirectory = BookmarkBase & {
readonly type : "directory" ,
}
export type BookmarkEntry = BookmarkInfo | BookmarkDirectory ;
2020-09-28 07:37:48 +00:00
export interface BookmarkEvents {
2021-03-14 18:39:08 +00:00
notify_bookmark_created : { bookmark : BookmarkEntry } ,
notify_bookmark_edited : { bookmark : BookmarkEntry , keys : ( keyof BookmarkInfo | keyof BookmarkDirectory ) [ ] } ,
notify_bookmark_deleted : { bookmark : BookmarkEntry , children : BookmarkEntry [ ] } ,
notify_bookmarks_imported : { bookmarks : BookmarkEntry [ ] } ,
2020-09-28 07:37:48 +00:00
}
2021-03-14 18:39:08 +00:00
export type OrderedBookmarkEntry = {
entry : BookmarkEntry ,
depth : number ,
childCount : number ,
} ;
2021-04-19 18:26:37 +00:00
const kStorageKey = "bookmarks_v2" ;
2021-03-14 18:39:08 +00:00
export class BookmarkManager {
readonly events : Registry < BookmarkEvents > ;
private readonly registeredBookmarks : BookmarkEntry [ ] ;
private defaultBookmarkCreated : boolean ;
constructor ( ) {
this . events = new Registry < BookmarkEvents > ( ) ;
this . registeredBookmarks = [ ] ;
this . defaultBookmarkCreated = false ;
}
2021-04-19 18:26:37 +00:00
async loadBookmarks() {
const bookmarksJson = await getStorageAdapter ( ) . get ( kStorageKey ) ;
2023-11-21 00:58:05 +00:00
if ( typeof bookmarksJson !== "string" ) {
2021-04-19 18:26:37 +00:00
const oldBookmarksJson = await getStorageAdapter ( ) . get ( "bookmarks" ) ;
2023-11-21 00:58:05 +00:00
if ( typeof oldBookmarksJson === "string" ) {
2021-03-14 18:39:08 +00:00
logDebug ( LogCategory . BOOKMARKS , tr ( "Found no new bookmarks but found old bookmarks. Trying to import." ) ) ;
try {
this . importOldBookmarks ( oldBookmarksJson ) ;
logInfo ( LogCategory . BOOKMARKS , tr ( "Successfully imported %d old bookmarks." ) , this . registeredBookmarks . length ) ;
2021-04-19 18:26:37 +00:00
await this . saveBookmarks ( ) ;
2021-03-14 18:39:08 +00:00
} catch ( error ) {
const saveKey = "bookmarks_v1_save_" + Date . now ( ) ;
logError ( LogCategory . BOOKMARKS , tr ( "Failed to import old bookmark data. Saving it as %s" ) , saveKey ) ;
2021-04-19 18:26:37 +00:00
await getStorageAdapter ( ) . set ( saveKey , oldBookmarksJson ) ;
2021-03-14 18:39:08 +00:00
} finally {
2021-04-19 18:26:37 +00:00
await getStorageAdapter ( ) . delete ( "bookmarks" ) ;
2021-03-14 18:39:08 +00:00
}
}
} else {
try {
const storageData = JSON . parse ( bookmarksJson ) ;
2023-11-21 00:58:05 +00:00
if ( storageData . version !== 2 ) {
2021-03-14 18:39:08 +00:00
throw tr ( "bookmark storage has an invalid version" ) ;
}
this . defaultBookmarkCreated = storageData . defaultBookmarkCreated ;
this . registeredBookmarks . slice ( 0 , this . registeredBookmarks . length ) ;
this . registeredBookmarks . push ( . . . storageData . bookmarks ) ;
logTrace ( LogCategory . BOOKMARKS , tr ( "Loaded %d bookmarks." ) , this . registeredBookmarks . length ) ;
} catch ( error ) {
const saveKey = "bookmarks_v2_save_" + Date . now ( ) ;
2021-04-19 18:26:37 +00:00
logError ( LogCategory . BOOKMARKS , tr ( "Failed to parse bookmarks. Saving them at %s and using a clean setup." ) , saveKey ) ;
await getStorageAdapter ( ) . set ( saveKey , bookmarksJson ) ;
await getStorageAdapter ( ) . delete ( kStorageKey ) ;
2021-03-14 18:39:08 +00:00
}
}
2023-11-21 00:58:05 +00:00
if ( ! this . defaultBookmarkCreated && this . registeredBookmarks . length === 0 ) {
2021-03-14 18:39:08 +00:00
this . defaultBookmarkCreated = true ;
logDebug ( LogCategory . BOOKMARKS , tr ( "No bookmarks found. Registering default bookmark." ) ) ;
this . createBookmark ( {
connectOnStartup : false ,
connectProfile : "default" ,
2023-11-21 00:58:05 +00:00
displayName : "Our LanPart<" ,
2021-03-14 18:39:08 +00:00
parentEntry : undefined ,
previousEntry : undefined ,
2023-11-21 00:58:05 +00:00
serverAddress : "tea.lp.kle.li" ,
2021-03-14 18:39:08 +00:00
serverPasswordHash : undefined ,
defaultChannel : undefined ,
defaultChannelPasswordHash : undefined ,
} ) ;
}
}
private importOldBookmarks ( jsonData : string ) {
const data = JSON . parse ( jsonData ) ;
2023-11-21 00:58:05 +00:00
if ( typeof data ? . root_bookmark !== "object" ) {
2021-03-14 18:39:08 +00:00
throw tr ( "missing root bookmark" ) ;
}
2023-11-21 00:58:05 +00:00
if ( ! Array . isArray ( data ? . root_bookmark ? . content ) ) {
2021-03-14 18:39:08 +00:00
throw tr ( "Missing root bookmarks content" ) ;
}
2023-11-21 00:58:05 +00:00
const registerBookmarks = ( parentEntry : string , previousEntry : string , entry : any ) : string | undefined = > {
if ( typeof entry . display_name !== "string" ) {
2021-03-14 18:39:08 +00:00
logWarn ( LogCategory . BOOKMARKS , tr ( "Missing display_name in old bookmark entry. Skipping entry." ) ) ;
return undefined ;
}
2023-11-21 00:58:05 +00:00
if ( "content" in entry ) {
2021-03-14 18:39:08 +00:00
/* it was a directory */
const directory = this . createDirectory ( {
previousEntry ,
parentEntry ,
displayName : entry.display_name ,
} ) ;
previousEntry = undefined ;
entry . content . forEach ( entry = > {
previousEntry = registerBookmarks ( directory . uniqueId , previousEntry , entry ) || previousEntry ;
} ) ;
} else {
/* it was a normal entry */
2023-11-21 00:58:05 +00:00
if ( typeof entry . connect_profile !== "string" ) {
2021-03-14 18:39:08 +00:00
logWarn ( LogCategory . BOOKMARKS , tr ( "Missing connect_profile in old bookmark entry. Skipping entry." ) ) ;
return undefined ;
}
2023-11-21 00:58:05 +00:00
if ( typeof entry . server_properties ? . server_address !== "string" ) {
2021-03-14 18:39:08 +00:00
logWarn ( LogCategory . BOOKMARKS , tr ( "Missing server_address in old bookmark entry. Skipping entry." ) ) ;
return undefined ;
}
2023-11-21 00:58:05 +00:00
if ( typeof entry . server_properties ? . server_port !== "number" ) {
2021-03-14 18:39:08 +00:00
logWarn ( LogCategory . BOOKMARKS , tr ( "Missing server_port in old bookmark entry. Skipping entry." ) ) ;
return undefined ;
}
let serverAddress ;
2023-11-21 00:58:05 +00:00
if ( entry . server_properties . server_address . indexOf ( ":" ) !== - 1 ) {
2021-03-14 18:39:08 +00:00
serverAddress = ` [ ${ entry . server_properties . server_address } ] ` ;
} else {
serverAddress = entry . server_properties . server_address ;
}
serverAddress += ":" + entry . server_properties . server_port ;
return this . createBookmark ( {
previousEntry ,
parentEntry ,
serverAddress ,
serverPasswordHash : entry.server_properties?.server_password_hash ,
defaultChannel : undefined ,
defaultChannelPasswordHash : undefined ,
displayName : entry.display_name ,
connectProfile : entry.connect_profile ,
connectOnStartup : false
} ) . uniqueId ;
2020-03-30 11:44:18 +00:00
}
2021-03-14 18:39:08 +00:00
}
let previousEntry = undefined ;
data . root_bookmark . content . forEach ( entry = > {
previousEntry = registerBookmarks ( undefined , previousEntry , entry ) || previousEntry ;
2021-01-10 13:21:38 +00:00
} ) ;
2021-03-14 18:39:08 +00:00
this . defaultBookmarkCreated = true ;
2018-12-18 19:00:29 +00:00
}
2021-04-19 18:26:37 +00:00
async saveBookmarks() {
await getStorageAdapter ( ) . set ( kStorageKey , JSON . stringify ( {
2021-03-14 18:39:08 +00:00
version : 2 ,
bookmarks : this.registeredBookmarks ,
defaultBookmarkCreated : this.defaultBookmarkCreated ,
2021-04-19 18:26:37 +00:00
} ) ) ;
2021-03-14 18:39:08 +00:00
}
2019-08-21 08:00:01 +00:00
2023-11-21 00:58:05 +00:00
getRegisteredBookmarks ( ) : BookmarkEntry [ ] {
2021-03-14 18:39:08 +00:00
return this . registeredBookmarks ;
}
2018-12-18 19:00:29 +00:00
2023-11-21 00:58:05 +00:00
getOrderedRegisteredBookmarks ( ) : OrderedBookmarkEntry [ ] {
2021-03-14 18:39:08 +00:00
const unorderedBookmarks = this . registeredBookmarks . slice ( 0 ) ;
const orderedBookmarks : OrderedBookmarkEntry [ ] = [ ] ;
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
const orderTreeLayer = ( entries : BookmarkEntry [ ] ) : BookmarkEntry [ ] = > {
2023-11-21 00:58:05 +00:00
if ( entries . length === 0 ) {
2021-03-14 18:39:08 +00:00
return [ ] ;
}
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
const result = [ ] ;
2023-11-21 00:58:05 +00:00
while ( entries . length > 0 ) {
2021-03-14 18:39:08 +00:00
let head = entries . find ( entry = > ! entry . previousEntry ) || entries [ 0 ] ;
2023-11-21 00:58:05 +00:00
while ( head ) {
2021-03-14 18:39:08 +00:00
result . push ( head ) ;
entries . remove ( head ) ;
head = entries . find ( entry = > entry . previousEntry === head . uniqueId ) ;
}
}
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
return result ;
}
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
const traverseTree = ( parentEntry : string | undefined , depth : number ) : number = > {
const children = unorderedBookmarks . filter ( e = > e . parentEntry === parentEntry ) ;
children . forEach ( child = > unorderedBookmarks . remove ( child ) ) ;
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
const childCount = children . length ;
2023-11-21 00:58:05 +00:00
for ( const entry of orderTreeLayer ( children ) ) {
2021-03-14 18:39:08 +00:00
let orderedEntry : OrderedBookmarkEntry = {
entry : entry ,
depth : depth ,
childCount : 0
} ;
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
orderedBookmarks . push ( orderedEntry ) ;
orderedEntry . childCount = traverseTree ( entry . uniqueId , depth + 1 ) ;
}
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
return childCount ;
} ;
2019-08-31 16:31:01 +00:00
2021-03-14 18:39:08 +00:00
traverseTree ( undefined , 0 ) ;
2019-08-31 16:31:01 +00:00
2021-03-14 18:39:08 +00:00
/* Append all broken/unreachable elements */
while ( unorderedBookmarks . length > 0 ) {
traverseTree ( unorderedBookmarks [ 0 ] . parentEntry , 0 ) ;
}
2018-12-28 14:39:23 +00:00
2021-03-14 18:39:08 +00:00
return orderedBookmarks ;
2018-12-18 19:00:29 +00:00
}
2023-11-21 00:58:05 +00:00
findBookmark ( uniqueId : string ) : BookmarkEntry | undefined {
2021-03-14 18:39:08 +00:00
return this . registeredBookmarks . find ( entry = > entry . uniqueId === uniqueId ) ;
}
2018-12-18 19:00:29 +00:00
2023-11-21 00:58:05 +00:00
createBookmark ( properties : Pick < BookmarkInfo , WritableKeys < BookmarkInfo > > ) : BookmarkInfo {
2021-03-14 18:39:08 +00:00
this . validateHangInPoint ( properties ) ;
const bookmark = Object . assign ( properties , {
uniqueId : guid ( ) ,
type : "entry"
} as BookmarkInfo ) ;
this . registeredBookmarks . push ( bookmark ) ;
this . events . fire ( "notify_bookmark_created" , { bookmark } ) ;
2021-04-19 18:26:37 +00:00
ignorePromise ( this . saveBookmarks ( ) ) ;
2021-03-14 18:39:08 +00:00
return bookmark ;
}
2018-12-18 19:00:29 +00:00
2021-03-14 18:39:08 +00:00
editBookmark ( uniqueId : string , newValues : Partial < Pick < BookmarkInfo , WritableKeys < BookmarkInfo > >> ) {
this . doEditBookmark ( uniqueId , newValues ) ;
2018-12-18 19:00:29 +00:00
}
2023-11-21 00:58:05 +00:00
createDirectory ( properties : Pick < BookmarkInfo , WritableKeys < BookmarkDirectory > > ) : BookmarkDirectory {
2021-03-14 18:39:08 +00:00
this . validateHangInPoint ( properties ) ;
const bookmark = Object . assign ( properties , {
uniqueId : guid ( ) ,
type : "directory"
} as BookmarkDirectory ) ;
this . registeredBookmarks . push ( bookmark ) ;
this . events . fire ( "notify_bookmark_created" , { bookmark } ) ;
2021-04-19 18:26:37 +00:00
ignorePromise ( this . saveBookmarks ( ) ) ;
2021-03-14 18:39:08 +00:00
return bookmark ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
editDirectory ( uniqueId : string , newValues : Partial < Pick < BookmarkDirectory , WritableKeys < BookmarkDirectory > >> ) {
this . doEditBookmark ( uniqueId , newValues ) ;
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
directoryContents ( uniqueId : string ) : BookmarkEntry [ ] {
2021-05-26 11:45:14 +00:00
return this . registeredBookmarks . filter ( bookmark = > bookmark . parentEntry === uniqueId ) ;
}
2021-03-14 18:39:08 +00:00
deleteEntry ( uniqueId : string ) {
const index = this . registeredBookmarks . findIndex ( entry = > entry . uniqueId === uniqueId ) ;
2023-11-21 00:58:05 +00:00
if ( index === - 1 ) {
2021-03-14 18:39:08 +00:00
return ;
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
const [ entry ] = this . registeredBookmarks . splice ( index , 1 ) ;
const children = [ ] , pendingChildren = [ entry ] ;
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
while ( pendingChildren [ 0 ] ) {
2021-03-14 18:39:08 +00:00
const child = pendingChildren . pop_front ( ) ;
children . push ( child ) ;
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
const childChildren = this . registeredBookmarks . filter ( entry = > entry . parentEntry === child . uniqueId ) ;
pendingChildren . push ( . . . childChildren ) ;
childChildren . forEach ( entry = > this . registeredBookmarks . remove ( entry ) ) ;
2020-03-30 11:44:18 +00:00
}
2021-03-14 18:39:08 +00:00
children . pop_front ( ) ;
this . events . fire ( "notify_bookmark_deleted" , { bookmark : entry , children } ) ;
2021-04-19 18:26:37 +00:00
ignorePromise ( this . saveBookmarks ( ) ) ;
2018-12-18 19:00:29 +00:00
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
executeConnect ( uniqueId : string , newTab : boolean ) {
const bookmark = this . findBookmark ( uniqueId ) ;
2023-11-21 00:58:05 +00:00
if ( ! bookmark || bookmark . type !== "entry" ) {
2021-03-14 18:39:08 +00:00
return ;
}
const connection = newTab ? server_connections . spawnConnectionHandler ( ) : server_connections . getActiveConnectionHandler ( ) ;
2023-11-21 00:58:05 +00:00
if ( ! connection ) {
2021-03-14 18:39:08 +00:00
return ;
}
let profile = findConnectProfile ( bookmark . connectProfile ) || defaultConnectProfile ( ) ;
connection . startConnectionNew ( {
profile : profile ,
targetAddress : bookmark.serverAddress ,
serverPasswordHashed : true ,
serverPassword : bookmark.serverPasswordHash ,
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
defaultChannel : bookmark.defaultChannel ,
defaultChannelPassword : bookmark.defaultChannelPasswordHash ,
defaultChannelPasswordHashed : true ,
token : undefined ,
nicknameSpecified : false ,
nickname : undefined
} , false ) . then ( undefined ) ;
}
executeAutoConnect() {
let newTab = server_connections . getActiveConnectionHandler ( ) . connection_state !== ConnectionState . UNCONNECTED ;
2023-11-21 00:58:05 +00:00
for ( const entry of this . getOrderedRegisteredBookmarks ( ) ) {
if ( entry . entry . type !== "entry" ) {
2021-03-14 18:39:08 +00:00
continue ;
}
2023-11-21 00:58:05 +00:00
if ( ! entry . entry . connectOnStartup ) {
2021-03-14 18:39:08 +00:00
continue ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
this . executeConnect ( entry . entry . uniqueId , newTab ) ;
newTab = true ;
2019-08-21 08:00:01 +00:00
}
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
exportBookmarks ( ) : string {
2021-03-14 18:39:08 +00:00
return JSON . stringify ( {
version : 1 ,
bookmarks : this.registeredBookmarks
} ) ;
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
importBookmarks ( filePayload : string ) : number {
2021-03-14 18:39:08 +00:00
let data ;
try {
data = JSON . parse ( filePayload )
} catch ( error ) {
throw tr ( "failed to parse bookmarks" ) ;
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
if ( data ? . version !== 1 ) {
2021-03-14 18:39:08 +00:00
throw tr ( "supplied data contains invalid version" ) ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
const newBookmarks = data . bookmarks as BookmarkEntry [ ] ;
2023-11-21 00:58:05 +00:00
if ( ! Array . isArray ( newBookmarks ) ) {
2021-03-14 18:39:08 +00:00
throw tr ( "missing bookmarks" ) ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
/* TODO: Validate integrity? */
2023-11-21 00:58:05 +00:00
for ( const knownBookmark of this . registeredBookmarks ) {
2021-03-14 18:39:08 +00:00
const index = newBookmarks . findIndex ( entry = > entry . uniqueId === knownBookmark . uniqueId ) ;
2023-11-21 00:58:05 +00:00
if ( index === - 1 ) {
2021-03-14 18:39:08 +00:00
continue ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
newBookmarks . splice ( index , 1 ) ;
}
2020-03-30 11:44:18 +00:00
2023-11-21 00:58:05 +00:00
if ( newBookmarks . length === 0 ) {
2021-03-14 18:39:08 +00:00
return ;
}
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
this . registeredBookmarks . push ( . . . newBookmarks ) ;
newBookmarks . forEach ( entry = > this . validateHangInPoint ( entry ) ) ;
this . events . fire ( "notify_bookmarks_imported" , { bookmarks : newBookmarks } ) ;
return newBookmarks . length ;
}
private doEditBookmark ( uniqueId : string , newValues : any ) {
const bookmarkInfo = this . findBookmark ( uniqueId ) ;
2023-11-21 00:58:05 +00:00
if ( ! bookmarkInfo ) {
2021-03-14 18:39:08 +00:00
return ;
}
const originalProperties = _ . cloneDeep ( bookmarkInfo ) ;
2023-11-21 00:58:05 +00:00
for ( const key of Object . keys ( newValues ) ) {
2021-03-14 18:39:08 +00:00
bookmarkInfo [ key ] = newValues [ key ] ;
}
this . validateHangInPoint ( bookmarkInfo ) ;
2020-03-30 11:44:18 +00:00
2021-03-14 18:39:08 +00:00
const editedKeys = [ ] ;
2023-11-21 00:58:05 +00:00
for ( const key of Object . keys ( newValues ) ) {
if ( _ . isEqual ( bookmarkInfo [ key ] , originalProperties [ key ] ) ) {
2021-03-14 18:39:08 +00:00
continue ;
2020-03-30 11:44:18 +00:00
}
2021-03-14 18:39:08 +00:00
editedKeys . push ( key ) ;
}
2023-11-21 00:58:05 +00:00
if ( editedKeys . length === 0 ) {
2021-03-14 18:39:08 +00:00
return ;
}
2021-04-19 18:26:37 +00:00
ignorePromise ( this . saveBookmarks ( ) ) ;
2021-03-14 18:39:08 +00:00
this . events . fire ( "notify_bookmark_edited" , { bookmark : bookmarkInfo , keys : editedKeys } ) ;
2020-03-30 11:44:18 +00:00
}
2021-03-14 18:39:08 +00:00
private validateHangInPoint ( entry : Partial < BookmarkBase > ) {
2023-11-21 00:58:05 +00:00
if ( entry . previousEntry ) {
2021-03-14 18:39:08 +00:00
const previousEntry = this . findBookmark ( entry . previousEntry ) ;
2023-11-21 00:58:05 +00:00
if ( ! previousEntry ) {
2021-03-14 18:39:08 +00:00
logError ( LogCategory . BOOKMARKS , tr ( "New bookmark previous entry does not exists. Clearing it." ) ) ;
entry . previousEntry = undefined ;
2023-11-21 00:58:05 +00:00
} else if ( previousEntry . parentEntry !== entry . parentEntry ) {
2021-03-14 18:39:08 +00:00
logWarn ( LogCategory . BOOKMARKS , tr ( "Previous entries parent does not match our entries parent. Updating our parent from %s to %s" ) , entry . parentEntry , previousEntry . parentEntry ) ;
entry . parentEntry = previousEntry . parentEntry ;
}
const openList = this . registeredBookmarks . filter ( e1 = > e1 . parentEntry === entry . parentEntry ) ;
let currentEntry = entry ;
2023-11-21 00:58:05 +00:00
while ( true ) {
if ( ! currentEntry . previousEntry ) {
2021-03-14 18:39:08 +00:00
break ;
}
const previousEntry = openList . find ( entry = > entry . uniqueId === currentEntry . previousEntry ) ;
2023-11-21 00:58:05 +00:00
if ( ! previousEntry ) {
2021-03-14 18:39:08 +00:00
logError ( LogCategory . BOOKMARKS , tr ( "Found circular dependency within the previous entry or one of the previous entries does not exists. Clearing out previous entry." ) ) ;
entry . previousEntry = undefined ;
break ;
}
openList . remove ( previousEntry ) ;
currentEntry = previousEntry ;
}
}
2023-11-21 00:58:05 +00:00
if ( entry . parentEntry ) {
2021-03-14 18:39:08 +00:00
const parentEntry = this . findBookmark ( entry . parentEntry ) ;
2023-11-21 00:58:05 +00:00
if ( ! parentEntry ) {
2021-03-14 18:39:08 +00:00
logError ( LogCategory . BOOKMARKS , tr ( "Missing parent entry %s. Clearing it." ) , entry . parentEntry ) ;
entry . parentEntry = undefined ;
}
const openList = this . registeredBookmarks . slice ( ) ;
let currentEntry = entry ;
2023-11-21 00:58:05 +00:00
while ( true ) {
if ( ! currentEntry . parentEntry ) {
2021-03-14 18:39:08 +00:00
break ;
}
const parentEntry = openList . find ( entry = > entry . uniqueId === currentEntry . parentEntry ) ;
2023-11-21 00:58:05 +00:00
if ( ! parentEntry ) {
2021-03-14 18:39:08 +00:00
logError ( LogCategory . BOOKMARKS , tr ( "Found circular dependency within a parent or one of the parents does not exists. Clearing out parent." ) ) ;
entry . parentEntry = undefined ;
break ;
}
openList . remove ( parentEntry ) ;
currentEntry = parentEntry ;
}
}
2023-11-21 00:58:05 +00:00
if ( entry . previousEntry ) {
2021-03-14 18:39:08 +00:00
this . registeredBookmarks . forEach ( bookmark = > {
2023-11-21 00:58:05 +00:00
if ( bookmark . previousEntry === entry . previousEntry && bookmark !== entry ) {
2021-03-14 18:39:08 +00:00
bookmark . previousEntry = bookmark . uniqueId ;
}
} ) ;
}
}
}
export let bookmarks : BookmarkManager ;
loader . register_task ( Stage . JAVASCRIPT_INITIALIZING , {
name : "initialize bookmarks" ,
function : async ( ) = > {
bookmarks = new BookmarkManager ( ) ;
2021-04-19 18:26:37 +00:00
await bookmarks . loadBookmarks ( ) ;
2021-03-14 18:39:08 +00:00
( window as any ) . bookmarks = bookmarks ;
} ,
priority : 20
2021-03-17 19:07:12 +00:00
} ) ;