diff --git a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift
index 5342efe13..65078b069 100644
--- a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift
+++ b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift
@@ -23,7 +23,7 @@ extension BWIBuildSettings {
secondaryAppName = "BundesMessenger"
settingsScreenShowLabSettings = true
authScreenShowRegister = true
- showTopBanner = false
+ showTopBanner = true
bwiShowDeveloperSettings = true
bwiPersonalState = true
bwiEnableErrorTracking = true
diff --git a/Riot/Assets/Images.xcassets/BWI/new_features.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/new_features.imageset/Contents.json
new file mode 100644
index 000000000..fff4ea669
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/BWI/new_features.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icon_new-releases.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/BWI/new_features.imageset/icon_new-releases.svg b/Riot/Assets/Images.xcassets/BWI/new_features.imageset/icon_new-releases.svg
new file mode 100644
index 000000000..68d70cc4c
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/BWI/new_features.imageset/icon_new-releases.svg
@@ -0,0 +1,10 @@
+
diff --git a/Riot/Assets/de.lproj/Bwi.strings b/Riot/Assets/de.lproj/Bwi.strings
index e97ff8c5a..b0d62ec38 100644
--- a/Riot/Assets/de.lproj/Bwi.strings
+++ b/Riot/Assets/de.lproj/Bwi.strings
@@ -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";
diff --git a/Riot/Assets/en.lproj/Bwi.strings b/Riot/Assets/en.lproj/Bwi.strings
index f7c082123..598f4ddc2 100644
--- a/Riot/Assets/en.lproj/Bwi.strings
+++ b/Riot/Assets/en.lproj/Bwi.strings
@@ -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";
diff --git a/Riot/Generated/BWIStrings.swift b/Riot/Generated/BWIStrings.swift
index 015e512bb..66650cf78 100644
--- a/Riot/Generated/BWIStrings.swift
+++ b/Riot/Generated/BWIStrings.swift
@@ -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)
diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift
index 4cf81539f..3a16e898b 100644
--- a/Riot/Generated/Images.swift
+++ b/Riot/Generated/Images.swift
@@ -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")
diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
index 2cf3969ea..282507835 100644
--- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
+++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
@@ -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)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
diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift b/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift
index ec11a5471..d0a3aa9f1 100644
--- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift
+++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift
@@ -32,4 +32,5 @@ import Foundation
case searchedRoom
case allChats
case unknown
+ case featureBanner
}
diff --git a/Riot/Modules/Common/Recents/RecentsBannerViewController.h b/Riot/Modules/Common/Recents/RecentsBannerViewController.h
deleted file mode 100644
index 031b45985..000000000
--- a/Riot/Modules/Common/Recents/RecentsBannerViewController.h
+++ /dev/null
@@ -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
diff --git a/Riot/Modules/Common/Recents/RecentsBannerViewController.m b/Riot/Modules/Common/Recents/RecentsBannerViewController.m
deleted file mode 100644
index a8259a972..000000000
--- a/Riot/Modules/Common/Recents/RecentsBannerViewController.m
+++ /dev/null
@@ -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
diff --git a/Riot/Modules/Favorites/FavouritesViewController.h b/Riot/Modules/Favorites/FavouritesViewController.h
index a9a68a4b2..3be388271 100644
--- a/Riot/Modules/Favorites/FavouritesViewController.h
+++ b/Riot/Modules/Favorites/FavouritesViewController.h
@@ -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;
diff --git a/Riot/Modules/Home/AllChats/AllChatsViewController.swift b/Riot/Modules/Home/AllChats/AllChatsViewController.swift
index ecfda48b7..1b69b5497 100644
--- a/Riot/Modules/Home/AllChats/AllChatsViewController.swift
+++ b/Riot/Modules/Home/AllChats/AllChatsViewController.swift
@@ -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.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 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
diff --git a/Riot/Modules/Home/HomeViewController.h b/Riot/Modules/Home/HomeViewController.h
index 0343e397c..842b79eb9 100644
--- a/Riot/Modules/Home/HomeViewController.h
+++ b/Riot/Modules/Home/HomeViewController.h
@@ -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
+@interface HomeViewController : RecentsViewController
+ (instancetype)instantiate;
diff --git a/Riot/Modules/People/PeopleViewController.h b/Riot/Modules/People/PeopleViewController.h
index 514333769..7e03c6313 100644
--- a/Riot/Modules/People/PeopleViewController.h
+++ b/Riot/Modules/People/PeopleViewController.h
@@ -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;
diff --git a/Riot/Modules/Rooms/RoomsViewController.h b/Riot/Modules/Rooms/RoomsViewController.h
index 68e79ea69..2f665ef65 100644
--- a/Riot/Modules/Rooms/RoomsViewController.h
+++ b/Riot/Modules/Rooms/RoomsViewController.h
@@ -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;
diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m
index ac9b24e3c..ddb60c185 100644
--- a/Riot/Modules/TabBar/MasterTabBarController.m
+++ b/Riot/Modules/TabBar/MasterTabBarController.m
@@ -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;
diff --git a/bwi/FeatureBanner/FeatureBannerView.swift b/bwi/FeatureBanner/FeatureBannerView.swift
new file mode 100644
index 000000000..38b3556a4
--- /dev/null
+++ b/bwi/FeatureBanner/FeatureBannerView.swift
@@ -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: UITableViewCell, FeatureBannerDelegate {
+ private var parentViewController: UIViewController?
+ private let hostingController = UIHostingController(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)
+ }
+
+}
diff --git a/bwi/TopBanner/FeatureBannerVisibilityService.swift b/bwi/TopBanner/FeatureBannerVisibilityService.swift
index 13a655bce..81d5b0d3e 100644
--- a/bwi/TopBanner/FeatureBannerVisibilityService.swift
+++ b/bwi/TopBanner/FeatureBannerVisibilityService.swift
@@ -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) {
diff --git a/bwi/TopBanner/TopBannerViewController.swift b/bwi/TopBanner/TopBannerViewController.swift
index d46593e91..f0571b8a6 100644
--- a/bwi/TopBanner/TopBannerViewController.swift
+++ b/bwi/TopBanner/TopBannerViewController.swift
@@ -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")
}