Merge branch 'feature/2638_new_layout_feature_banner' into 'develop'

MESSENGER-2638 new layout feature banner

See merge request bwmessenger/bundesmessenger/bundesmessenger-ios!111
This commit is contained in:
JanNiklas Grabowski
2023-03-28 10:38:04 +00:00
20 changed files with 341 additions and 242 deletions
@@ -23,7 +23,7 @@ extension BWIBuildSettings {
secondaryAppName = "BundesMessenger"
settingsScreenShowLabSettings = true
authScreenShowRegister = true
showTopBanner = false
showTopBanner = true
bwiShowDeveloperSettings = true
bwiPersonalState = true
bwiEnableErrorTracking = true
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon_new-releases.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
@@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_100_3886)">
<path d="M23 11.495L20.56 8.70569L20.9 5.01338L17.29 4.19063L15.4 1L12 2.46488L8.6 1L6.71 4.19063L3.1 5.00334L3.44 8.69565L1 11.495L3.44 14.2843L3.1 17.9866L6.71 18.8094L8.6 22L12 20.5251L15.4 21.99L17.29 18.7993L20.9 17.9766L20.56 14.2843L23 11.495ZM18.49 13.612L18.75 16.4114L16.01 17.0334L14.58 19.4515L12 18.3378L9.42 19.4515L7.99 17.0334L5.25 16.4114L5.51 13.602L3.66 11.495L5.51 9.36789L5.25 6.57859L7.99 5.96655L9.42 3.54849L12 4.65217L14.58 3.53846L16.01 5.95652L18.75 6.57859L18.49 9.37793L20.34 11.495L18.49 13.612ZM11 14.505H13V16.5117H11V14.505ZM11 6.47826H13V12.4983H11V6.47826Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_100_3886">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 855 B

+3
View File
@@ -517,6 +517,9 @@
"bwi_settings_new_features_header" = "Neue Funktionen";
"bwi_settings_new_features_show_features" = "Neue Funktionen anzeigen";
"bwi_feature_banner_header" = "Neue Funktionen";
"bwi_feature_banner_show_more_button" = "Erfahre mehr";
"bwi_feature_banner_advertisement_text" = "Du kannst jetzt aktive und vergangene Umfragen gesammelt in den Raumdetails einsehen (erreichbar unter Raumdetails, im Bereich \"Umfrageverlauf\").";
// MARK: - Onboarding
"onboarding_splash_login_button_title" = "Loslegen";
+3
View File
@@ -397,6 +397,9 @@
"bwi_settings_new_features_header" = "New Features";
"bwi_settings_new_features_show_features" = "Show new features";
"bwi_feature_banner_header" = "New Features";
"bwi_feature_banner_show_more_button" = "Learn more";
"bwi_feature_banner_advertisement_text" = "You can now see a poll history in the room details.";
// MARK: - Onboarding
"onboarding_splash_login_button_title" = "Let's go";
+12
View File
@@ -203,6 +203,18 @@ public class BWIL10n: NSObject {
public static func bwiErrorInviteGeneral(_ p1: String) -> String {
return BWIL10n.tr("Bwi", "bwi_error_invite_general", p1)
}
/// Du kannst jetzt aktive und vergangene Umfragen gesammelt in den Raumdetails einsehen (erreichbar unter Raumdetails, im Bereich "Umfrageverlauf").
public static var bwiFeatureBannerAdvertisementText: String {
return BWIL10n.tr("Bwi", "bwi_feature_banner_advertisement_text")
}
/// Neue Funktionen
public static var bwiFeatureBannerHeader: String {
return BWIL10n.tr("Bwi", "bwi_feature_banner_header")
}
/// Erfahre mehr
public static var bwiFeatureBannerShowMoreButton: String {
return BWIL10n.tr("Bwi", "bwi_feature_banner_show_more_button")
}
/// Der angegebene Server ist nicht für die Nutzung mit dem %@ vorgesehen
public static func bwiLoginProtectionErrorMessage(_ p1: String) -> String {
return BWIL10n.tr("Bwi", "bwi_login_protection_error_message", p1)
+1
View File
@@ -48,6 +48,7 @@ internal class Asset: NSObject {
internal static let birthdayCake = ImageAsset(name: "birthday_cake")
internal static let fileAttachmentIcon = ImageAsset(name: "file_attachment_icon")
internal static let fileScanInfected = ImageAsset(name: "file_scan_infected")
internal static let newFeatures = ImageAsset(name: "new_features")
internal static let qrcodeViewfinder = ImageAsset(name: "qrcode_viewfinder")
internal static let welcomeExperience1 = ImageAsset(name: "welcome_experience_1")
internal static let welcomeExperience2 = ImageAsset(name: "welcome_experience_2")
@@ -56,6 +56,9 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
// Timer to not refresh publicRoomsDirectoryDataSource on every keystroke.
NSTimer *publicRoomsTriggerTimer;
// bwi: new feature banner
bool shouldHideFeatureBanner;
}
@property (nonatomic, strong, readwrite) RecentsDataSourceSections *sections;
@@ -77,6 +80,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
- (instancetype)initWithMatrixSession:(MXSession *)mxSession
recentsListService:(id<RecentsListServiceProtocol>)theRecentsListService
{
// bwi: new feature banner
shouldHideFeatureBanner = TRUE;
if (self = [super initWithMatrixSession:mxSession])
{
processingQueue = dispatch_queue_create("RecentsDataSource", DISPATCH_QUEUE_SERIAL);
@@ -101,6 +106,9 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
[self registerAllChatsSettingsUpdateNotification];
self.allChatsFilterOptions = [AllChatsFilterOptions new];
// bwi: new feature banner
[self registerFeatureBannerReadStatusChangedNotification];
[self shouldShowFeatureBanner];
}
return self;
}
@@ -109,6 +117,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
{
[self unregisterSpaceServiceDidBuildGraphNotification];
[self unregisterAllChatsSettingsUpdateNotification];
// bwi: new feature banner
[self unregisterFeatureBannerNotification];
}
#pragma mark - Properties
@@ -221,6 +231,11 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
- (RecentsDataSourceSections *)makeDataSourceSections
{
NSMutableArray *types = [NSMutableArray array];
// bwi: add new feature banner
if (!shouldHideFeatureBanner && BWIBuildSettings.shared.showTopBanner){
[types addObject:@(RecentsDataSourceSectionTypeFeatureBanner)];
}
if (self.recentsDataSourceMode == RecentsDataSourceModeRoomInvites)
{
[types addObject:@(RecentsDataSourceSectionTypeInvites)];
@@ -643,6 +658,10 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
NSUInteger count = 0;
RecentsDataSourceSectionType sectionType = [self.sections sectionTypeForSectionIndex:section];
// bwi: new feature banner
if (sectionType == RecentsDataSourceSectionTypeFeatureBanner) {
count = 1;
}
if (sectionType == RecentsDataSourceSectionTypeCrossSigningBanner && self.crossSigningBannerDisplay != CrossSigningBannerDisplayNone)
{
count = 1;
@@ -725,7 +744,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
(sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeAllChats) ||
(sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount) ||
(sectionType == RecentsDataSourceSectionTypeAllChats && self.currentSpace != nil && self.currentSpace.childRoomIds.count == 0) ||
sectionType == RecentsDataSourceSectionTypePersonalNotes )
sectionType == RecentsDataSourceSectionTypePersonalNotes || sectionType == RecentsDataSourceSectionTypeFeatureBanner)
{
return 0.0;
}
@@ -1911,4 +1930,63 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
return NO;
}
#pragma mark - bwi new feature banner
- (void) shouldShowFeatureBanner
{
if (BWIBuildSettings.shared.showTopBanner){
MXSession* session = [self mxSession];
if(!session)
return;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
FeatureBannerVisibilityService *featureBannerService = [[FeatureBannerVisibilityService alloc] initWithMxSession:session];
[featureBannerService isUnreadWithVersion:version completion:^(BOOL unread) {
if (unread) {
// this notification will be called either if the user clicked on the banner or wants to hide it
[[NSNotificationCenter defaultCenter] addObserverForName:@"de.bwi.messenger.hide_top_banner" object:NULL queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
[self markFeatureBannerAsRead];
}];
// this notification will be called either if the user clicked on the banner or wants to hide it using a swipe gesture
[[NSNotificationCenter defaultCenter] addObserverForName:@"de.bwi.messenger.mark_top_banner_as_read" object:NULL queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
[self markFeatureBannerAsRead];
}];
self->shouldHideFeatureBanner = FALSE;
} else {
self->shouldHideFeatureBanner = TRUE;
}
[self.delegate dataSource:self didCellChange:nil];
}];
} else {
self->shouldHideFeatureBanner = TRUE;
}
}
- (void) registerFeatureBannerReadStatusChangedNotification
{
[[NSNotificationCenter defaultCenter] addObserverForName:@"de.bwi.messenger.mark_top_banner_as_unread" object:NULL queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
[self shouldShowFeatureBanner];
}];
}
- (void) markFeatureBannerAsRead
{
MXSession* session = [self mxSession];
if(!session)
return;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
FeatureBannerVisibilityService *featureBannerService = [[FeatureBannerVisibilityService alloc] initWithMxSession:session];
[featureBannerService markAsReadWithVersion:version];
self->shouldHideFeatureBanner = TRUE;
[self.delegate dataSource:self didCellChange:nil];
}
- (void)unregisterFeatureBannerNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"de.bwi.messenger.mark_top_banner_as_read" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"de.bwi.messenger.hide_top_banner" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"de.bwi.messenger.mark_top_banner_as_unread" object:nil];
}
@end
@@ -32,4 +32,5 @@ import Foundation
case searchedRoom
case allChats
case unknown
case featureBanner
}
@@ -1,25 +0,0 @@
/*
* Copyright (c) 2021 BWI GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "RecentsViewController.h"
@interface RecentsBannerViewController : RecentsViewController
+ (bool)isBannerHidden;
- (void)setupTopBanner;
@end
@@ -1,193 +0,0 @@
/*
* Copyright (c) 2021 BWI GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "RecentsBannerViewController.h"
#import "GeneratedInterface-Swift.h"
@interface RecentsBannerViewController ()
{
TopBannerViewController *topBannerViewController;
bool bannersInitialized;
bool isBannerVisible;
bool shouldHideBanner;
}
- (void)setTopBannerViewConstraints;
- (void)showTopBanner;
- (void)hideTopBanner;
@end
@implementation RecentsBannerViewController
+ (bool)isBannerHidden {
NSDictionary *info = [[NSBundle mainBundle] infoDictionary];
NSString *version = [info objectForKey:@"CFBundleShortVersionString"];
if( [version isEqual:[[NSUserDefaults standardUserDefaults] objectForKey:@"de.bwi.messenger.top_banner_confirmation"]] )
return TRUE;
else
return FALSE;
}
- (void)viewDidLoad
{
[super viewDidLoad];
bannersInitialized = FALSE;
isBannerVisible = FALSE;
shouldHideBanner = FALSE;
}
- (void)onMatrixSessionChange
{
[super onMatrixSessionChange];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if( [BWIBuildSettings.shared showTopBanner] ) {
[self showTopBanner];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if( bannersInitialized && [BWIBuildSettings.shared showTopBanner] ) {
[self hideTopBanner];
}
}
- (void)setTopBannerViewConstraints
{
// get the offset in the y-coordiante so that the banner appears below the navigation bar
UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
CGFloat statusBarHeight = window.safeAreaInsets.top;
CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
NSLayoutConstraint *alignTopConstraint = [NSLayoutConstraint
constraintWithItem:topBannerViewController.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view.safeAreaLayoutGuide
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[[self view] addConstraint:alignTopConstraint];
NSLayoutConstraint *alignLeadingConstraint = [NSLayoutConstraint
constraintWithItem:topBannerViewController.view
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[[self view] addConstraint:alignLeadingConstraint];
NSLayoutConstraint *alignTrailingConstraint = [NSLayoutConstraint
constraintWithItem:topBannerViewController.view
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
[[self view] addConstraint:alignTrailingConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint
constraintWithItem:topBannerViewController.view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:NULL
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:130];
[[self view] addConstraint:heightConstraint];
}
- (void)setupTopBanner
{
NSMutableArray *bannerControllers = [NSMutableArray array];
MXSession* session = [self mainSession];
if( !session || bannersInitialized)
return;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
FeatureBannerVisibilityService *service = [[FeatureBannerVisibilityService alloc] initWithMxSession:session];
[service isUnreadWithVersion:version completion:^(BOOL unread) {
if (unread) {
// [bannerControllers addObject:[[FeatureBannerViewController alloc] initWithSession:session version:version]];
self->topBannerViewController = [[TopBannerViewController alloc] initWithControllers: bannerControllers];
// this notification will be called either if the user clicked on the banner or wants to hide it using a swipe gesture
[[NSNotificationCenter defaultCenter] addObserverForName:@"de.bwi.messenger.hide_top_banner" object:NULL queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
[self hideTopBanner];
[self->topBannerViewController removeAdvertiseViewControllers];
}];
// this notification will be called either if the user clicked on the banner or wants to hide it using a swipe gesture
[[NSNotificationCenter defaultCenter] addObserverForName:@"de.bwi.messenger.mark_top_banner_as_read" object:NULL queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {
self->shouldHideBanner = TRUE;
}];
self->bannersInitialized = TRUE;
if( [BWIBuildSettings.shared showTopBanner] ) {
[self showTopBanner];
}
}
}];
}
- (void)showTopBanner
{
if( isBannerVisible || shouldHideBanner) {
return; // nothing to do because the banner is already visible for the user
}
if( [topBannerViewController.advertiseViewControllers count] > 0 )
{
// add child view controller
[self addChildViewController:topBannerViewController];
[[topBannerViewController view] setTranslatesAutoresizingMaskIntoConstraints:FALSE];
[[topBannerViewController view] setBounds:CGRectMake(0, 0, self.view.bounds.size.width, 100)];
[[self view] addSubview:[topBannerViewController view]];
[topBannerViewController didMoveToParentViewController:self];
[self setTopBannerViewConstraints];
isBannerVisible = TRUE;
}
}
- (void)hideTopBanner
{
if( [[topBannerViewController advertiseViewControllers] count] > 0 )
{
// remove child view controller
[topBannerViewController willMoveToParentViewController:nil];
[topBannerViewController.view removeFromSuperview];
[topBannerViewController removeFromParentViewController];
}
isBannerVisible = FALSE;
}
@end
@@ -15,12 +15,12 @@
limitations under the License.
*/
#import "RecentsBannerViewController.h"
#import "RecentsViewController.h"
/**
The `FavouritesViewController` screen is the view controller displayed when `Favourites` tab is selected.
*/
@interface FavouritesViewController : RecentsBannerViewController
@interface FavouritesViewController : RecentsViewController
+ (instancetype)instantiate;
@@ -61,6 +61,9 @@ class AllChatsViewController: HomeViewController {
private let reviewSessionAlertSnoozeController = ReviewSessionAlertSnoozeController()
// bwi: new feature banner height
private var featureBannerViewHeight: CGFloat = 0
private var bannerView: UIView? {
didSet {
bannerView?.translatesAutoresizingMaskIntoConstraints = false
@@ -123,6 +126,7 @@ class AllChatsViewController: HomeViewController {
recentsTableView.register(RecentEmptySectionTableViewCell.nib, forCellReuseIdentifier: RecentEmptySectionTableViewCell.reuseIdentifier)
recentsTableView.register(RecentEmptySpaceSectionTableViewCell.nib, forCellReuseIdentifier: RecentEmptySpaceSectionTableViewCell.reuseIdentifier)
recentsTableView.register(RecentsInvitesTableViewCell.nib, forCellReuseIdentifier: RecentsInvitesTableViewCell.reuseIdentifier)
recentsTableView.register(FeatureBannerViewCell<FeatureBannerView>.self, forCellReuseIdentifier: "featureBanner")
recentsTableView.contentInsetAdjustmentBehavior = .automatic
toolbarHeight = toolbar.frame.height
@@ -284,7 +288,8 @@ class AllChatsViewController: HomeViewController {
RecentsDataSourceSectionType.serverNotice.rawValue,
RecentsDataSourceSectionType.suggestedRooms.rawValue,
RecentsDataSourceSectionType.breadcrumbs.rawValue,
RecentsDataSourceSectionType.personalNotes.rawValue
RecentsDataSourceSectionType.personalNotes.rawValue,
RecentsDataSourceSectionType.featureBanner.rawValue
]
}
@@ -348,15 +353,27 @@ class AllChatsViewController: HomeViewController {
guard let sectionType = sectionType(forSectionAt: section), sectionType == .invites else {
return super.tableView(tableView, numberOfRowsInSection: section)
}
if sectionType == .featureBanner {
return 1
}
return dataSource?.tableView(tableView, numberOfRowsInSection: section) ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites else {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites || sectionType == .featureBanner else {
return super.tableView(tableView, cellForRowAt: indexPath)
}
// bwi: feature banner cell
if sectionType == .featureBanner {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "featureBanner", for: indexPath) as? FeatureBannerViewCell<FeatureBannerView> else {
return UITableViewCell()
}
cell.selectionStyle = .none
cell.setupView(parent: self, rootView: FeatureBannerView(delegate: cell))
featureBannerViewHeight = cell.calculateHeight()
return cell
}
guard let dataSource = dataSource else {
MXLog.failure("Missing data source")
return UITableViewCell()
@@ -367,20 +384,27 @@ class AllChatsViewController: HomeViewController {
// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites else {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites || sectionType == .featureBanner else {
return super.tableView(tableView, heightForRowAt: indexPath)
}
return dataSource?.cellHeight(at: indexPath) ?? 0
if sectionType == .featureBanner {
return featureBannerViewHeight
} else {
return dataSource?.cellHeight(at: indexPath) ?? 0
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites else {
guard let sectionType = sectionType(forSectionAt: indexPath.section), sectionType == .invites || sectionType == .featureBanner else {
super.tableView(tableView, didSelectRowAt: indexPath)
return
}
showRoomInviteList()
if sectionType == .invites {
showRoomInviteList()
} else {
// bwi: feature banner is not selectable
return
}
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
@@ -1035,7 +1059,7 @@ extension AllChatsViewController: SplitViewMasterViewControllerProtocol {
func bwiOnUnlockedByPin() {
// bwi specific
self.setupTopBanner()
}
// MARK: - Private
+2 -2
View File
@@ -15,13 +15,13 @@
limitations under the License.
*/
#import "RecentsBannerViewController.h"
#import "RecentsViewController.h"
#import "RecentsDataSource.h"
/**
The `HomeViewController` screen is the main app screen.
*/
@interface HomeViewController : RecentsBannerViewController <UITableViewDataSource, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@interface HomeViewController : RecentsViewController <UITableViewDataSource, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
+ (instancetype)instantiate;
+2 -2
View File
@@ -15,13 +15,13 @@
limitations under the License.
*/
#import "RecentsBannerViewController.h"
#import "RecentsViewController.h"
#import "ContactsDataSource.h"
/**
'PeopleViewController' instance is used to display/filter the direct rooms and a list of contacts.
*/
@interface PeopleViewController : RecentsBannerViewController
@interface PeopleViewController : RecentsViewController
+ (instancetype)instantiate;
+2 -2
View File
@@ -15,12 +15,12 @@
limitations under the License.
*/
#import "RecentsBannerViewController.h"
#import "RecentsViewController.h"
/**
The `RoomsViewController` screen is the view controller displayed when `Rooms` tab is selected.
*/
@interface RoomsViewController : RecentsBannerViewController
@interface RoomsViewController : RecentsViewController
+ (instancetype)instantiate;
@@ -1036,14 +1036,11 @@
switch (tabItemTag)
{
case TABBAR_FAVOURITES_INDEX:
[self.favouritesViewController setupTopBanner];
[self.favouritesViewController checkAppVersionOutdated];
break;
case TABBAR_PEOPLE_INDEX:
[self.peopleViewController setupTopBanner];
break;
case TABBAR_ROOMS_INDEX:
[self.roomsViewController setupTopBanner];
break;
default:
break;
+169
View File
@@ -0,0 +1,169 @@
//
/*
* Copyright (c) 2023 BWI GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import SwiftUI
import UIKit
protocol FeatureBannerDelegate {
func didPressClose()
func didPressShowDetails()
}
@objcMembers class FeatureBannerViewCell<Content: View>: UITableViewCell, FeatureBannerDelegate {
private var parentViewController: UIViewController?
private let hostingController = UIHostingController<Content?>(rootView: nil)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
hostingController.view.backgroundColor = UIColor.clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setupView(parent: UIViewController, rootView: Content) {
self.parentViewController = parent
hostingController.rootView = rootView
let shouldControllerMove = hostingController.parent != parent
if shouldControllerMove {
removeController()
parent.addChild(hostingController)
}
if !self.contentView.subviews.contains(hostingController.view) {
self.contentView.addSubview(hostingController.view)
self.contentView.vc_addSubViewMatchingParentSafeArea(hostingController.view)
}
if shouldControllerMove {
hostingController.didMove(toParent: parent)
}
}
func calculateHeight() -> CGFloat {
hostingController.view.setNeedsLayout()
hostingController.view.layoutIfNeeded()
let result = hostingController.view.intrinsicContentSize.height
return result
}
private func removeController() {
hostingController.willMove(toParent: nil)
hostingController.view.removeFromSuperview()
hostingController.removeFromParent()
parentViewController = nil
}
deinit {
removeController()
}
// MARK: user interaction
func hideTopBanner() {
removeController()
NotificationCenter.default.post(name: .bwiHideTopBanner, object: self, userInfo: ["type" : "feature_banner"])
}
func didPressClose() {
hideTopBanner()
}
func didPressShowDetails() {
let htmlFile = Bundle.main.path(forResource: BWIBuildSettings.shared.newFeaturesHTML, ofType: "html")
if let webviewController = WebViewViewController(localHTMLFile: htmlFile) {
webviewController.title = BWIL10n.bwiSettingsNewFeaturesHeader
hostingController.parent?.present(webviewController, animated: true, completion: { () -> Void in
self.hideTopBanner()
})
}
}
}
struct FeatureBannerView: View {
var delegate: FeatureBannerDelegate?
var body: some View {
VStack(alignment: .center) {
closeButton
header
advertisementText
showMoreButton
}
.background(Color(ThemeService.shared().theme.tintColor))
.cornerRadius(12)
.padding(16)
}
var header: some View {
HStack() {
Image(Asset.Images.newFeatures.name)
.renderingMode(.template)
.foregroundColor(.white)
Text(BWIL10n.bwiFeatureBannerHeader)
.font(.system(size: 20).bold())
.foregroundColor(.white)
}
}
var advertisementText: some View {
Text(BWIL10n.bwiFeatureBannerAdvertisementText)
.font(.system(size: 15))
.multilineTextAlignment(.center)
.lineLimit(nil)
.foregroundColor(.white)
.padding(5)
}
var closeButton: some View {
HStack() {
Spacer()
Button {
delegate?.didPressClose()
} label: {
Image(Asset.Images.closeButton.name)
.renderingMode(.template)
.foregroundColor(.white)
.padding(10)
}
}
}
var showMoreButton: some View {
Button {
delegate?.didPressShowDetails()
} label: {
Text(BWIL10n.bwiFeatureBannerShowMoreButton)
.font(.system(size: 15))
.padding(10)
.foregroundColor(.white)
.overlay(RoundedRectangle(cornerRadius: 12)
.stroke(Color.white, lineWidth: 2))
}
.padding(.top, 10)
.padding(.bottom, 25)
}
}
@@ -50,7 +50,10 @@ import Foundation
featureDict[version] = true
return session.setAccountData(featureDict, forType: AccountDataTypes.featureVisibility, success: nil, failure: nil)
return session.setAccountData(featureDict, forType: AccountDataTypes.featureVisibility, success: {
// bwi: update tableview
NotificationCenter.default.post(name: .bwiMarkTopBannerAsUnRead, object: nil, userInfo: ["type" : "feature_banner"])
}, failure: nil)
}
func isUnread( version: String, completion: @escaping (_ unread : Bool) -> Void) {
@@ -85,4 +85,5 @@ extension TopBannerViewController: UIPageViewControllerDelegate {
extension NSNotification.Name {
static let bwiHideTopBanner = Notification.Name("de.bwi.messenger.hide_top_banner")
static let bwiMarkTopBannerAsRead = Notification.Name("de.bwi.messenger.mark_top_banner_as_read")
static let bwiMarkTopBannerAsUnRead = Notification.Name("de.bwi.messenger.mark_top_banner_as_unread")
}