chromium/ios/chrome/browser/ui/tab_switcher/tab_group_item.mm

// Copyright 2024 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/ui/tab_switcher/tab_group_item.h"

#import "base/task/sequenced_task_runner.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group_range.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/group_tab_info.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_group_utils.h"

@implementation TabGroupItem {
  WebStateList* _webStateList;
  NSMutableDictionary<NSNumber*, GroupTabInfo*>* _tabGroupInfos;
  base::WeakPtr<const TabGroup> _tabGroup;
  const void* _tabGroupIdentifier;
  NSUUID* _requestUUID;
}

- (instancetype)initWithTabGroup:(const TabGroup*)tabGroup
                    webStateList:(WebStateList*)webStateList {
  CHECK(tabGroup);
  CHECK(webStateList);
  CHECK(webStateList->ContainsGroup(tabGroup));
  self = [super init];
  if (self) {
    _tabGroup = tabGroup->GetWeakPtr();
    _tabGroupIdentifier = tabGroup;
    _webStateList = webStateList;
    _tabGroupInfos = [[NSMutableDictionary alloc] init];
  }
  return self;
}

- (const void*)tabGroupIdentifier {
  return _tabGroupIdentifier;
}

- (const TabGroup*)tabGroup {
  return _tabGroup.get();
}

- (NSString*)title {
  if (!_tabGroup) {
    return nil;
  }
  return _tabGroup->GetTitle();
}

- (UIColor*)groupColor {
  if (!_tabGroup) {
    return nil;
  }
  return _tabGroup->GetColor();
}

- (UIColor*)foregroundColor {
  if (!_tabGroup) {
    return nil;
  }
  return _tabGroup->GetForegroundColor();
}

- (NSInteger)numberOfTabsInGroup {
  if (!_tabGroup) {
    return 0;
  }
  return _tabGroup->range().count();
}

- (BOOL)collapsed {
  if (!_tabGroup) {
    return NO;
  }
  return _tabGroup->visual_data().is_collapsed();
}

- (void)fetchGroupTabInfos:(GroupTabInfosFetchingCompletionBlock)completion {
  if (!_tabGroup) {
    __weak TabGroupItem* weakSelf = self;
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(^{
          completion(weakSelf, nil);
        }));
    return;
  }

  // Reset the array to ensure to not display old snapshots and or favicons.
  [_tabGroupInfos removeAllObjects];
  NSUUID* UUID = [NSUUID UUID];
  _requestUUID = UUID;
  NSUInteger numberOfRequests = MIN(7, _tabGroup->range().count());
  int firstIndex = _tabGroup->range().range_begin();
  for (NSUInteger requestIndex = 0; requestIndex < numberOfRequests;
       requestIndex++) {
    web::WebState* webState =
        _webStateList->GetWebStateAt(firstIndex + requestIndex);
    CHECK(webState);
    __weak TabGroupItem* weakSelf = self;
    [TabGroupUtils fetchTabGroupInfoFromWebState:webState
                                      completion:^(GroupTabInfo* info) {
                                        [weakSelf
                                            groupTabInfoFetched:info
                                               forRequestNumber:requestIndex
                                                numberOfRequest:numberOfRequests
                                                     completion:completion
                                                    requestUUID:UUID];
                                      }];
  }
}

#pragma mark - Debugging

- (NSString*)description {
  return [NSString stringWithFormat:@"Group Title: %@", self.title];
}

#pragma mark - Private helpers

// Called when `info` for the `requestNumber` out of `numberOfRequests` is
// fetched for the `requestUUID`.
- (void)groupTabInfoFetched:(GroupTabInfo*)info
           forRequestNumber:(NSUInteger)requestNumber
            numberOfRequest:(NSUInteger)numberOfRequests
                 completion:(GroupTabInfosFetchingCompletionBlock)completion
                requestUUID:(NSUUID*)requestUUId {
  if (![requestUUId isEqual:_requestUUID] || !_tabGroup) {
    return;
  }

  _tabGroupInfos[@(requestNumber)] = info;

  if (_tabGroupInfos.count != numberOfRequests) {
    return;
  }

  auto comparator = ^NSComparisonResult(NSNumber* obj1, NSNumber* obj2) {
    return [obj1 compare:obj2];
  };
  NSMutableArray* infos = [NSMutableArray array];
  for (NSNumber* key in
       [[_tabGroupInfos allKeys] sortedArrayUsingComparator:comparator]) {
    [infos addObject:_tabGroupInfos[key]];
  }
  completion(self, infos);
}

@end