mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-14 11:49:59 +02:00
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:
@@ -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 |
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user