chromium/ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_mediator.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_mediator.h"

#import "base/containers/contains.h"
#import "base/memory/raw_ptr.h"
#import "components/bookmarks/browser/bookmark_model.h"
#import "components/bookmarks/browser/bookmark_node.h"
#import "components/bookmarks/common/bookmark_features.h"
#import "ios/chrome/browser/bookmarks/model/bookmark_model_bridge_observer.h"
#import "ios/chrome/browser/bookmarks/model/bookmark_storage_type.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/bookmark_ui_constants.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/bookmark_utils_ios.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_consumer.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_mediator_delegate.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_mutator.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/folder_chooser/bookmarks_folder_chooser_sub_data_source_impl.h"
#import "ios/chrome/browser/signin/model/authentication_service_observer_bridge.h"
#import "ios/chrome/browser/sync/model/sync_observer_bridge.h"

using bookmarks::BookmarkNode;

@interface BookmarksFolderChooserMediator () <
    AuthenticationServiceObserving,
    BookmarksFolderChooserMutator,
    BookmarksFolderChooserParentDataSource,
    SyncObserverModelBridge>
@end

@implementation BookmarksFolderChooserMediator {
  raw_ptr<bookmarks::BookmarkModel> _bookmarkModel;
  // Data source from local bookmark nodes.
  BookmarksFolderChooserSubDataSourceImpl* _localOrSyncableDataSource;
  // Data source from account bookmark nodes.
  BookmarksFolderChooserSubDataSourceImpl* _accountDataSource;
  // Set of nodes to hide when displaying folders. This is to avoid to move a
  // folder inside a child folder. These are also the list of nodes that are
  // being edited (moved to a folder).
  std::set<const BookmarkNode*> _editedNodes;
  // Observer for signin status changes.
  std::unique_ptr<AuthenticationServiceObserverBridge> _authServiceBridge;
  // Sync service.
  raw_ptr<syncer::SyncService> _syncService;
  // Observer for sync service status changes.
  std::unique_ptr<SyncObserverBridge> _syncObserverBridge;
}

- (instancetype)initWithBookmarkModel:(bookmarks::BookmarkModel*)model
                          editedNodes:(std::set<const BookmarkNode*>)editedNodes
                authenticationService:(AuthenticationService*)authService
                          syncService:(syncer::SyncService*)syncService {
  DCHECK(model);
  DCHECK(model->loaded());
  DCHECK(authService->initialized());

  self = [super init];
  if (self) {
    _bookmarkModel = model;
    _localOrSyncableDataSource =
        [[BookmarksFolderChooserSubDataSourceImpl alloc]
            initWithBookmarkModel:model
                             type:BookmarkStorageType::kLocalOrSyncable
                 parentDataSource:self];
    _accountDataSource = [[BookmarksFolderChooserSubDataSourceImpl alloc]
        initWithBookmarkModel:model
                         type:BookmarkStorageType::kAccount
             parentDataSource:self];

    _editedNodes = std::move(editedNodes);
    _authServiceBridge = std::make_unique<AuthenticationServiceObserverBridge>(
        authService, self);
    _syncService = syncService;
    _syncObserverBridge.reset(new SyncObserverBridge(self, syncService));
  }
  return self;
}

- (void)disconnect {
  [_localOrSyncableDataSource disconnect];
  _localOrSyncableDataSource.consumer = nil;
  _localOrSyncableDataSource = nil;
  [_accountDataSource disconnect];
  _accountDataSource.consumer = nil;
  _accountDataSource = nil;
  _editedNodes.clear();
  _authServiceBridge.reset();
  _syncService = nullptr;
  _syncObserverBridge = nullptr;
}

- (void)dealloc {
  DCHECK(!_localOrSyncableDataSource);
}

- (const std::set<const BookmarkNode*>&)editedNodes {
  return _editedNodes;
}

#pragma mark - BookmarksFolderChooserDataSource

- (id<BookmarksFolderChooserSubDataSource>)accountDataSource {
  DCHECK([self shouldShowAccountBookmarks]);
  return _accountDataSource;
}

- (id<BookmarksFolderChooserSubDataSource>)localOrSyncableDataSource {
  return _localOrSyncableDataSource;
}

- (BOOL)shouldDisplayCloudIconForLocalOrSyncableBookmarks {
  return bookmark_utils_ios::IsAccountBookmarkStorageOptedIn(_syncService);
}

- (BOOL)shouldShowAccountBookmarks {
  return bookmark_utils_ios::IsAccountBookmarkStorageAvailable(_bookmarkModel);
}

#pragma mark - BookmarksFolderChooserMutator

- (void)setSelectedFolderNode:(const BookmarkNode*)folderNode {
  _selectedFolderNode = folderNode;
  [_consumer notifyModelUpdated];
}

#pragma mark - BookmarksFolderChooserParentDataSource

- (void)bookmarkNodeDeleted:(const BookmarkNode*)bookmarkNode {
  // Remove node from `_editedNodes` if it is already deleted (possibly remotely
  // by another sync device).
  if (base::Contains(_editedNodes, bookmarkNode)) {
    _editedNodes.erase(bookmarkNode);
    // if `_editedNodes` becomes empty, nothing to move.  Exit the folder
    // chooser.
    if (_editedNodes.empty()) {
      [_delegate bookmarksFolderChooserMediatorWantsDismissal:self];
    }
    // Exit here because no visible node was deleted. Nodes in `_editedNodes`
    // cannot be any visible folder in folder chooser.
    return;
  }

  if (!bookmarkNode->is_folder()) {
    return;
  }

  if (bookmarkNode == _selectedFolderNode) {
    // The selected folder has been deleted. Unset `_selectedFolderNode`.
    _selectedFolderNode = nil;
  }
}

- (void)bookmarkModelWillRemoveAllNodes {
  _editedNodes.clear();
  _selectedFolderNode = nil;
  // Nothing to move so exit the folder chooser.
  [_delegate bookmarksFolderChooserMediatorWantsDismissal:self];
}

#pragma mark - AuthenticationServiceObserving

- (void)onServiceStatusChanged {
  [_consumer notifyModelUpdated];
}

#pragma mark - SyncObserverModelBridge

- (void)onSyncStateChanged {
  [_consumer notifyModelUpdated];
}

#pragma mark - Property

- (void)setConsumer:(id<BookmarksFolderChooserConsumer>)consumer {
  _consumer = consumer;
  _localOrSyncableDataSource.consumer = consumer;
  _accountDataSource.consumer = consumer;
}

@end