Feature/1801 ruhezeit einstellungsmenu

This commit is contained in:
Arnfried Griesert
2022-06-02 03:41:21 +00:00
parent 810124110b
commit c20966c2ab
26 changed files with 770 additions and 566 deletions

View File

@@ -468,9 +468,6 @@ final class BuildSettings: NSObject {
// MARK: Room Member Details Screen (bwi=true)
static let roomMemberDetailsHideLeaveButton : Bool = true
// MARK: Rest time feature (bwi=false)
static let featureWorkTime : Bool = false
// MARK: Room create options (bwi=false)
static let enableShowInRoomDirectory : Bool = false

View File

@@ -242,4 +242,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 82644f3408b6874a1dcd71e332ed7b8d18cac2f5
COCOAPODS: 1.11.3
COCOAPODS: 1.10.1

View File

@@ -4,7 +4,8 @@
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

View File

@@ -38,6 +38,33 @@
"bwi_mdm_logout_message" = "Die Konfiguration hat sich geändert. Bitte melde dich neu an.";
// MARK - Notification Times bwi
"bwi_notification_times_monday" = "Montag";
"bwi_notification_times_tuesday" = "Dienstag";
"bwi_notification_times_wednesday" = "Mittwoch";
"bwi_notification_times_thursday" = "Donnerstag";
"bwi_notification_times_friday" = "Freitag";
"bwi_notification_times_saturday" = "Samstag";
"bwi_notification_times_sunday" = "Sonntag";
"bwi_notification_times_monday_short" = "Mo";
"bwi_notification_times_tuesday_short" = "Di";
"bwi_notification_times_wednesday_short" = "Mi";
"bwi_notification_times_thursday_short" = "Do";
"bwi_notification_times_friday_short" = "Fr";
"bwi_notification_times_saturday_short" = "Sa";
"bwi_notification_times_sunday_short" = "So";
"bwi_notification_times" = "Benachrichtigungszeiten";
"bwi_notification_times_done_action" = "Fertig";
"bwi_notification_times_toggle_title" = "Benachrichtigungszeiten";
"bwi_notification_times_toggle_description" = "Außerhalb der eingestellten Benachrichtigungszeiten erhälst du keine Benachrichtigungen. Diese Regel kannst du pro Raum und Chat ein- und ausschalten.";
"bwi_notification_times_start_time" = "von";
"bwi_notification_times_end_time" = "bis";
"bwi_notification_times_button_activate" = "Am %@ benachrichtigen";
"bwi_notification_times_button_deactivate" = "Am %@ nicht benachrichtigen";
"bwi_notification_times_status_activated" = "Am %@ bekommst du Benachrichtigungen";
"bwi_notification_times_status_deactivated" = "Am %@ bekommst du keine Benachrichtigungen";
"bwi_room_member_details_userlabel" = "Funktion in %@: %@";
"bwi_room_member_section_userlabels" = "Funktion in %@";
"bwi_room_member_userlabels_description" = "Als Administrator kannst du für Personen im Raum eine Funktion hinzufügen und entfernen.\nDiese ist für alle Raummitglieder sichtbar.";

View File

@@ -1449,15 +1449,8 @@
"room_message_replying_to" = "%@ anworten";
"room_message_editing" = "Bearbeitung";
// MARK - Rest Time bwi
"settings_enable_rest_time" = "Ruhezeitregelung";
"room_details_main_section_rest_time" = "Ruhezeitregelung";
"room_details_main_section_rest_time_global_disabled" = "Aktiviere die Ruhezeitregelung in den Profileinstellungen";
"room_details_main_section_rest_time_global" = "";
"room_details_main_section_rest_time_locally_enabled" = "";
"room_details_main_section_rest_time_locally_disabled" = "Nicht aktiv";
"room_details_main_section_rest_time_restday" = "heute Ruhetag";
"room_details_main_section_rest_time_time" = "%@ - %@";
// MARK - Notification Times bwi
"settings_enable_notification_times" = "Benachrichtigungszeiten";
"space_beta_announce_information" = "Spaces bieten neue Möglichkeiten um Räume und Personen zu gruppieren. Sie sind noch nicht auf iOS verfügbar, aber du kannst sie jetzt schon mit Web und Desktop nutzen.";
"space_feature_unavailable_information" = "Spaces bieten neue Möglichkeiten um Räume und Personen zu gruppieren.\n\nBald werden sie auch hier verfügbar sein. Für den Moment kannst du ihnen auf einer der anderen Plattformen beitreten und hier auf alle Räume zugreifen, denen du dort beitrittst.";

View File

@@ -38,6 +38,31 @@
"bwi_mdm_logout_message" = "The configuration has changed. Please reconnect.";
// MARK - Notification Times bwi
"bwi_notification_times_monday" = "Monday";
"bwi_notification_times_tuesday" = "Tuesday";
"bwi_notification_times_wednesday" = "Wednesday";
"bwi_notification_times_thursday" = "Thursday";
"bwi_notification_times_friday" = "Friday";
"bwi_notification_times_saturday" = "Saturday";
"bwi_notification_times_sunday" = "Sunday";
"bwi_notification_times_monday_short" = "Mo";
"bwi_notification_times_tuesday_short" = "Tu";
"bwi_notification_times_wednesday_short" = "We";
"bwi_notification_times_thursday_short" = "Th";
"bwi_notification_times_friday_short" = "Fr";
"bwi_notification_times_saturday_short" = "Sa";
"bwi_notification_times_sunday_short" = "Su";
"bwi_notification_times" = "Notification Times";
"bwi_notification_times_done_action" = "Done";
"bwi_notification_times_toggle_title" = "Notification Times";
"bwi_notification_times_toggle_description" = "You will not receive notifications outside the set notification times. You can switch this rule on and off per room and chat.";
"bwi_notification_times_start_time" = "from";
"bwi_notification_times_end_time" = "till";
"bwi_notification_times_button_activate" = "Activate notifications on %@";
"bwi_notification_times_button_deactivate" = "Deactivate notifications on %@";
"bwi_room_member_details_userlabel" = "Function in %@: %@";
"bwi_room_member_section_userlabels" = "Function in %@";
"bwi_room_member_userlabels_description" = "As an admin, you can add and remove a function label for a room member. This function label is visible to all room members.";

View File

@@ -1942,15 +1942,8 @@ Tap the + to start adding people.";
"space_beta_announce_subtitle" = "The new version of communities";
"space_beta_announce_information" = "Spaces are a new way to group rooms and people. Theyre not on iOS yet, but you can use them now on Web and Desktop.";
// MARK - Rest Time bwi
"settings_enable_rest_time" = "Off-time";
"room_details_main_section_rest_time" = "Off-time";
"room_details_main_section_rest_time_global_disabled" = "Activate the off-time setting in the preferences";
"room_details_main_section_rest_time_global" = "";
"room_details_main_section_rest_time_locally_enabled" = "";
"room_details_main_section_rest_time_locally_disabled" = "";
"room_details_main_section_rest_time_restday" = "Day off today";
"room_details_main_section_rest_time_time" = "%@ - %@";
// MARK - Notification Times bwi
"settings_enable_notification_times" = "Notification Times";
"spaces_home_space_title" = "Home";
"spaces_add_space_title" = "Create space";

View File

@@ -4947,34 +4947,6 @@ public class VectorL10n: NSObject {
public static var roomDetailsLowPriorityTag: String {
return VectorL10n.tr("Vector", "room_details_low_priority_tag")
}
/// Off-time
public static var roomDetailsMainSectionRestTime: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time")
}
///
public static var roomDetailsMainSectionRestTimeGlobal: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_global")
}
/// Activate the off-time setting in the preferences
public static var roomDetailsMainSectionRestTimeGlobalDisabled: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_global_disabled")
}
///
public static var roomDetailsMainSectionRestTimeLocallyDisabled: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_locally_disabled")
}
///
public static var roomDetailsMainSectionRestTimeLocallyEnabled: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_locally_enabled")
}
/// Day off today
public static var roomDetailsMainSectionRestTimeRestday: String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_restday")
}
/// %@ - %@
public static func roomDetailsMainSectionRestTimeTime(_ p1: String, _ p2: String) -> String {
return VectorL10n.tr("Vector", "room_details_main_section_rest_time_time", p1, p2)
}
/// Mute notifications
public static var roomDetailsMuteNotifs: String {
return VectorL10n.tr("Vector", "room_details_mute_notifs")
@@ -6887,6 +6859,10 @@ public class VectorL10n: NSObject {
public static var settingsEnableInappNotifications: String {
return VectorL10n.tr("Vector", "settings_enable_inapp_notifications")
}
/// Notification Times
public static var settingsEnableNotificationTimes: String {
return VectorL10n.tr("Vector", "settings_enable_notification_times")
}
/// Notifications on this device
public static var settingsEnablePushNotif: String {
return VectorL10n.tr("Vector", "settings_enable_push_notif")
@@ -6899,10 +6875,6 @@ public class VectorL10n: NSObject {
public static var settingsEnableRageshake: String {
return VectorL10n.tr("Vector", "settings_enable_rageshake")
}
/// Off-time
public static var settingsEnableRestTime: String {
return VectorL10n.tr("Vector", "settings_enable_rest_time")
}
/// Show room events
public static var settingsEnableRoomAvatar: String {
return VectorL10n.tr("Vector", "settings_enable_room_avatar")

View File

@@ -199,8 +199,66 @@ class RiotSharedSettings: NSObject {
return session.setAccountData(allowedWidgetsDict, forType: Settings.allowedWidgets, success: success, failure: failure)
}
// MARK: Top Banner Features
// MARK: Notification Times
@available(iOS 14.0, *)
func fetchNotificationTimes() {
guard let dict = getAccountData(forEventType: "de.bwi.notification_times") else {
return
}
NotificationTimes.shared.isEnabled = (dict["is_enabled"] as? Bool) ?? false
if let entries = dict["entries"] as? [[String: Any]], entries.count == 7 {
for i in 0...6 {
let weekday = NotificationTimes.shared.weekday(index: i)
weekday.isEnabled = (entries[i]["is_enabled"] as? Bool) ?? false
if let fromHour = entries[i]["from_hour"] as? Int, let fromMinute = entries[i]["from_minute"] as? Int {
weekday.startTime = Date.from(hour: fromHour, minute: fromMinute)
}
if let toHour = entries[i]["to_hour"] as? Int, let toMinute = entries[i]["to_minute"] as? Int {
weekday.endTime = Date.from(hour: toHour, minute: toMinute)
}
}
}
if let rooms = dict["rooms"] as? [[String: Any]] {
NotificationTimes.shared.rooms = [:]
for room in rooms {
if let roomID = room["room_id"] as? String, let isEnabled = room["is_enabled"] as? Bool {
NotificationTimes.shared.rooms[roomID] = isEnabled
}
}
}
}
@available(iOS 14.0, *)
func storeNotificationTimes(success: @escaping () -> Void, failure: @escaping (Error?) -> Void) -> MXHTTPOperation? {
let eventType = "de.bwi.notification_times"
var entries: [[String: Any]] = []
for i in 0...6 {
let weekday = NotificationTimes.shared.weekday(index: i)
let calender = Calendar.current
let fromHour = calender.component(.hour, from: weekday.startTime)
let fromMinute = calender.component(.minute, from: weekday.startTime)
let toHour = calender.component(.hour, from: weekday.endTime)
let toMinute = calender.component(.minute, from: weekday.endTime)
let entry: [String: Any] = ["is_enabled": weekday.isEnabled, "from_our": fromHour, "from_minute": fromMinute, "to_hour": toHour, "to_minute": toMinute]
entries.append(entry)
}
var rooms: [[String: Any]] = []
for room in NotificationTimes.shared.rooms {
rooms.append(["room_id": room.key, "is_enabled": room.value])
}
let dict: [String: Any] = ["is_enabled": NotificationTimes.shared.isEnabled, "entries": entries, "rooms": rooms]
return session.setAccountData(dict, forType: eventType, success: success, failure: failure)
}
// MARK: Top Banner Features
func topBanner(for feature: String) -> Bool {
guard let featuresDict = getAccountData(forEventType: "de.bwi.top_banner_features") else {
return true

View File

@@ -1168,8 +1168,8 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
UIContextualAction *muteAction = nil;
if (BuildSettings.featureWorkTime) {
muteAction = [self muteActionWithWorkTime:room];
if (BwiBuildSettings.bwiNotificationTimes) {
muteAction = [self muteActionWithNotificationTime:room];
} else {
BOOL isMuted = room.isMute || room.isMentionsOnly;
@@ -1201,7 +1201,6 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
notificationImage = [UIImage imageNamed:@"room_action_notification"];
}
// $$$
notificationImage = [notificationImage vc_tintedImageUsingColor:isMuted ? unselectedColor : selectedColor];
muteAction.image = [notificationImage vc_notRenderedImage];
}
@@ -2483,19 +2482,33 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
#pragma mark - Bwi Rest Time
- (UIContextualAction*) muteActionWithWorkTime:(MXRoom*)room {
BOOL isMuted = room.isMute || room.isMentionsOnly;
BOOL isWorkTime = [self isWorkTimeActive:room];
- (UIContextualAction*) muteActionWithNotificationTime:(MXRoom*)room {
if (@available(iOS 14.0, *)) {
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:room.mxSession];
[sharedSettings fetchNotificationTimes];
}
BOOL isMuted = room.isMute || room.isMentionsOnly;
BOOL isRoomNotificationTimesActive = [self isNotficationTimesActive:room];
UIContextualAction *muteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
title:@""
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
[self muteEditedRoomNotificationsWithWorkTime:!isMuted];
if ([BuildSettings roomSettingsScreenShowNotificationsV2])
{
[self changeEditedRoomNotificationSettings];
}
else
{
[self muteEditedRoomNotifications:!isMuted];
}
completionHandler(YES);
}];
muteAction.backgroundColor = ThemeService.shared.theme.baseColor;
muteAction.image =[self editedRoomNotififcationImageForMuted:isMuted andWorkTime:isWorkTime];
muteAction.image =[self editedRoomNotififcationImageForMuted:isMuted andRoomNotificationTimeActive:isRoomNotificationTimesActive];
return muteAction;
}
@@ -2530,11 +2543,11 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[self cancelEditionMode:self->isRefreshPending];
}];
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUserId];
if ([service isWorkTimeGlobalyEnabled]) {
[service activateWorkTimeInRoom:editedRoomId];
}
// WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUserId];
//
// if ([service isWorkTimeGlobalyEnabled]) {
// [service activateWorkTimeInRoom:editedRoomId];
// }
}
}
else
@@ -2545,29 +2558,25 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
}
}
- (BOOL) isWorkTimeActive:(MXRoom*)room {
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
if ([service isWorkTimeGlobalyEnabled]) {
if ([service.store isWorkTimeRoom:room.roomId]) {
return [service.store isWorkTimeInRoomActive:room.roomId];
} else {
return !room.isDirect;
}
- (BOOL) isNotficationTimesActive:(MXRoom*)room {
if (@available(iOS 14.0, *)) {
return [[NotificationTimes shared] isEnabled] && [[NotificationTimes shared] isEnabledForRoomWithRoomID:room.roomId isDirect:room.isDirect];
} else {
return NO;
return false;
}
}
- (UIImage*)editedRoomNotififcationImageForMuted:(BOOL)isMuted andWorkTime:(BOOL)WorkTimeActive {
- (UIImage*)editedRoomNotififcationImageForMuted:(BOOL)isMuted andRoomNotificationTimeActive:(BOOL)isRoomNotificationTimeActive {
UIImage *image = nil;
if (WorkTimeActive && !isMuted ) {
image = [[UIImage imageNamed:@"room_action_resttime"] vc_tintedImageUsingColor:isMuted ? ThemeService.shared.theme.tabBarUnselectedItemTintColor
: ThemeService.shared.theme.tintColor];
} else {
image = [[UIImage imageNamed:@"room_action_notification"] vc_tintedImageUsingColor:isMuted ? ThemeService.shared.theme.tabBarUnselectedItemTintColor
: ThemeService.shared.theme.tintColor];
if (isRoomNotificationTimeActive && !isMuted) {
image = [[UIImage imageNamed:@"room_action_resttime"] vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor];
}
else if (isMuted) {
image = [[UIImage imageNamed:@"room_action_notification_muted"] vc_tintedImageUsingColor: ThemeService.shared.theme.tabBarUnselectedItemTintColor];
}
else {
image = [[UIImage imageNamed:@"room_action_notification"] vc_tintedImageUsingColor: ThemeService.shared.theme.tintColor];
}
return [image vc_notRenderedImage];

View File

@@ -114,7 +114,6 @@ NSString *const kRoomSettingsNameKey = @"kRoomSettingsNameKey";
NSString *const kRoomSettingsTopicKey = @"kRoomSettingsTopicKey";
NSString *const kRoomSettingsTagKey = @"kRoomSettingsTagKey";
NSString *const kRoomSettingsMuteNotifKey = @"kRoomSettingsMuteNotifKey";
NSString *const kRoomSettingsWorkTimeNotifKey = @"kRoomSettingsWorkTimeNotifKey";
NSString *const kRoomSettingsDirectChatKey = @"kRoomSettingsDirectChatKey";
NSString *const kRoomSettingsJoinRuleKey = @"kRoomSettingsJoinRuleKey";
NSString *const kRoomSettingsGuestAccessKey = @"kRoomSettingsGuestAccessKey";
@@ -127,6 +126,7 @@ NSString *const kRoomSettingsNewRelatedGroupKey = @"kRoomSettingsNewRelatedGroup
NSString *const kRoomSettingsRemovedRelatedGroupKey = @"kRoomSettingsRemovedRelatedGroupKey";
NSString *const kRoomSettingsEncryptionKey = @"kRoomSettingsEncryptionKey";
NSString *const kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey = @"kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey";
NSString *const kRoomSettingsNotificationTimesKey = @"kRoomSettingsNotificationTimesKey";
NSString *const kRoomSettingsNameCellViewIdentifier = @"kRoomSettingsNameCellViewIdentifier";
NSString *const kRoomSettingsTopicCellViewIdentifier = @"kRoomSettingsTopicCellViewIdentifier";
@@ -490,6 +490,14 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
- (void)updateSections
{
if (@available(iOS 14.0, *)) {
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
if(session) {
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
[sharedSettings fetchNotificationTimes];
}
}
if(mxRoom.isPersonalNotesRoom) {
[self updateSectionsAsPersonalNotes];
return;
@@ -546,7 +554,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
{
[sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT];
}
if (BuildSettings.featureWorkTime)
if (BwiBuildSettings.bwiNotificationTimes)
{
[sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_REST_TIME];
[sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_REST_TIME_INFO];
@@ -2210,6 +2218,26 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
BOOL blacklistUnverifiedDevices = [((NSNumber*)updatedItemsDict[kRoomSettingsEncryptionBlacklistUnverifiedDevicesKey]) boolValue];
[mxRoom.mxSession.crypto setBlacklistUnverifiedDevicesInRoom:mxRoom.roomId blacklist:blacklistUnverifiedDevices];
}
// Notification times toggle
if (updatedItemsDict[kRoomSettingsNotificationTimesKey])
{
if (@available(iOS 14.0, *)) {
bool isON = [[updatedItemsDict[kRoomSettingsNotificationTimesKey] stringValue] isEqualToString:@"ON"];
if(isON) {
[[NotificationTimes shared] enableForRoomWithRoomID:self.roomId];
} else {
[[NotificationTimes shared] disableForRoomWithRoomID:self.roomId];
}
RiotSharedSettings *sharedSetting = [[RiotSharedSettings alloc] initWithSession:self.mainSession];
(void)[sharedSetting storeNotificationTimesWithSuccess:^{} failure:^(NSError *error) {
MXLogWarning(@"[RoomSettingsViewController] Cannot store value for notification times toggle.")
}];
}
[updatedItemsDict removeObjectForKey:kRoomSettingsNotificationTimesKey];
}
}
[self getNavigationItem].rightBarButtonItem.enabled = NO;
@@ -2309,10 +2337,10 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
cell = roomNotifCell;
}
else if( row == ROOM_SETTINGS_MAIN_SECTION_ROW_REST_TIME) {
cell = [self cellForWorkTimeSwitch:tableView indexPath:indexPath];
cell = [self cellForNotificationTimesSwitch:tableView indexPath:indexPath];
}
else if( row == ROOM_SETTINGS_MAIN_SECTION_ROW_REST_TIME_INFO) {
cell = [self cellForWorkTimeInfo:tableView indexPath:indexPath];
cell = [self cellForNotificationTimesInfo:tableView indexPath:indexPath];
}
else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT)
{
@@ -4308,35 +4336,34 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
return roomAccessToggleCell;
}
- (UITableViewCell*) cellForWorkTimeSwitch:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCellWithLabelAndSwitch *workTimeToggleCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
- (UITableViewCell*) cellForNotificationTimesSwitch:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCellWithLabelAndSwitch *toggleCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
[workTimeToggleCell.mxkSwitch addTarget:self action:@selector(toggleWorkTime:) forControlEvents:UIControlEventValueChanged];
[toggleCell.mxkSwitch addTarget:self action:@selector(toggleNotificationTimes:) forControlEvents:UIControlEventValueChanged];
workTimeToggleCell.mxkLabel.text = NSLocalizedStringFromTable(@"room_details_main_section_rest_time", @"Vector", nil);
workTimeToggleCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
workTimeToggleCell.mxkSwitch.on = [self isWorkTimeSwitchOn];
workTimeToggleCell.mxkSwitch.enabled = [self isWorkTimeSwitchEnabled];
toggleCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_notification_times", @"Vector", nil);
toggleCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
toggleCell.mxkSwitch.on = [self isNotificationTimesSwitchOn];
toggleCell.mxkSwitch.enabled = [self isNotificationTimesSwitchEnabled];
return workTimeToggleCell;
return toggleCell;
}
- (UITableViewCell*) cellForWorkTimeInfo:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCell *workTimeInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
if (!workTimeInfoCell)
- (UITableViewCell*) cellForNotificationTimesInfo:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCell *notificationTimesInfoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
if (!notificationTimesInfoCell)
{
workTimeInfoCell = [[MXKTableViewCell alloc] init];
notificationTimesInfoCell = [[MXKTableViewCell alloc] init];
}
workTimeInfoCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
workTimeInfoCell.textLabel.font = [UIFont systemFontOfSize:15.0];
workTimeInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
workTimeInfoCell.userInteractionEnabled = NO;
workTimeInfoCell.textLabel.text = [self textForWorkTimeInfoCell];
workTimeInfoCell.textLabel.adjustsFontSizeToFitWidth = YES;
return workTimeInfoCell;
notificationTimesInfoCell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
notificationTimesInfoCell.textLabel.font = [UIFont systemFontOfSize:15.0];
notificationTimesInfoCell.selectionStyle = UITableViewCellSelectionStyleNone;
notificationTimesInfoCell.userInteractionEnabled = NO;
notificationTimesInfoCell.textLabel.text = NSLocalizedStringFromTable(@"bwi_notification_times_toggle_description", @"Bwi", @"");
notificationTimesInfoCell.textLabel.numberOfLines = 0;
return notificationTimesInfoCell;
}
- (void)toggleDirectoryAccessBW:(UISwitch*)theSwitch
@@ -4366,63 +4393,29 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
}
- (void)toggleWorkTime:(UISwitch*)theSwitch {
NSLog(@"Toggle Worktime");
- (void)toggleNotificationTimes:(UISwitch*)theSwitch {
NSLog(@"Toggle Notification Times");
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
updatedItemsDict[kRoomSettingsWorkTimeNotifKey] = @(theSwitch.on);
if (theSwitch.on) {
[service activateWorkTimeInRoom:self.roomId];
} else {
[service deactivateWorkTimeInRoom:self.roomId];
if (@available(iOS 14.0, *)) {
updatedItemsDict[kRoomSettingsNotificationTimesKey] = theSwitch.on ? @"ON" : @"OFF";
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
}
[self getNavigationItem].rightBarButtonItem.enabled = (updatedItemsDict.count != 0);
}
- (NSString*) textForWorkTimeInfoCell {
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
NSString *text = @"";
if (service.isWorkTimeGlobalyEnabled) {
if ([service.store isWorkTimeRoom:self.roomId] ) {
if ([service.store isWorkTimeInRoomActive:self.roomId]) {
text = [NSString stringWithFormat:@"%@ %@", NSLocalizedStringFromTable(@"room_details_main_section_rest_time_locally_enabled", @"Vector", nil), [service localizedWorkTime]];
} else {
text = NSLocalizedStringFromTable(@"room_details_main_section_rest_time_locally_disabled", @"Vector", nil);
}
} else {
text = [NSString stringWithFormat:@"%@ %@", NSLocalizedStringFromTable(@"room_details_main_section_rest_time_global", @"Vector", nil), [service localizedWorkTime]];
}
- (BOOL) isNotificationTimesSwitchOn {
if (@available(iOS 14.0, *)) {
RiotSharedSettings *sharedSetting = [[RiotSharedSettings alloc] initWithSession:self.mainSession];
[sharedSetting fetchNotificationTimes];
return [[NotificationTimes shared] isEnabledForRoomWithRoomID:self.roomId isDirect:mxRoom.isDirect];
} else {
text = NSLocalizedStringFromTable(@"room_details_main_section_rest_time_global_disabled", @"Vector", nil);
return false;
}
return text;
}
- (BOOL) isWorkTimeSwitchOn {
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
BOOL isOn = NO;
if (service.isWorkTimeGlobalyEnabled) {
if ([service.store isWorkTimeRoom:self.roomId] ) {
if ([service.store isWorkTimeInRoomActive:self.roomId]) {
isOn = YES;
}
} else {
isOn = !self->mxRoom.isDirect;
}
}
return isOn;
}
- (BOOL) isWorkTimeSwitchEnabled {
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
return service.isWorkTimeGlobalyEnabled;
- (BOOL) isNotificationTimesSwitchEnabled {
return !mxRoom.isDirect;
}
- (void) updateSectionsAsPersonalNotes {

View File

@@ -121,13 +121,13 @@ typedef NS_ENUM(NSUInteger, LINKS_SHOW_URL_PREVIEWS)
typedef NS_ENUM(NSUInteger, NOTIFICATION_SETTINGS)
{
NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX = 0,
NOTIFICATION_SETTINGS_ENABLE_REST_TIME,
NOTIFICATION_SETTINGS_SYSTEM_SETTINGS,
NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT,
NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX,
NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX,
NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX,
NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX,
NOTIFICATION_SETTINGS_NOTIFICATION_TIMES_INDEX,
NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX,
};
@@ -491,11 +491,6 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX];
if (BuildSettings.featureWorkTime)
{
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_REST_TIME];
}
if (RiotSettings.shared.settingsScreenShowSystemSettingsOption) {
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SYSTEM_SETTINGS];
}
@@ -521,6 +516,9 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
if (RiotSettings.shared.settingsNotificationsShowMentions) {
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX];
}
if (BwiBuildSettings.bwiNotificationTimes) {
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_NOTIFICATION_TIMES_INDEX];
}
if (RiotSettings.shared.settingsNotificationsShowAdvanced) {
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX];
}
@@ -2245,10 +2243,6 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_ENABLE_REST_TIME)
{
cell = [self cellForWorkTime:tableView indexPath:indexPath];
}
else if (row == NOTIFICATION_SETTINGS_SYSTEM_SETTINGS)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
@@ -2302,7 +2296,7 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX)
else if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_NOTIFICATION_TIMES_INDEX)
{
cell = [self getDefaultTableViewCell:tableView];
if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX)
@@ -2313,6 +2307,10 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
{
cell.textLabel.text = [VectorL10n settingsMentionsAndKeywords];
}
else if (row == NOTIFICATION_SETTINGS_NOTIFICATION_TIMES_INDEX)
{
cell.textLabel.text = [VectorL10n settingsEnableNotificationTimes];
}
else if (row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX)
{
cell.textLabel.text = [VectorL10n settingsOther];
@@ -3264,6 +3262,9 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
case NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX:
[self showNotificationSettings:NotificationSettingsScreenMentionsAndKeywords];
break;
case NOTIFICATION_SETTINGS_NOTIFICATION_TIMES_INDEX:
[self showNotificationTimesSettings];
break;
case NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX:
[self showNotificationSettings:NotificationSettingsScreenOther];
break;
@@ -4356,7 +4357,6 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
RiotSettings.shared.showNSFWPublicRooms = sender.isOn;
}
- (void)showDeveloperSettings
{
if (@available(iOS 14.0, *)) {
@@ -4365,6 +4365,14 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
}
}
- (void)showNotificationTimesSettings
{
if (@available(iOS 14.0, *)) {
UIViewController *notificationTimesViewController = [NotificationTimesViewController makeViewControllerWithSession:self.mainSession];
[self pushViewController:notificationTimesViewController];
}
}
- (void)toggleEnableRoomMessageBubbles:(UISwitch *)sender
{
RiotSettings.shared.roomScreenEnableMessageBubbles = sender.isOn;
@@ -5334,26 +5342,6 @@ ThreadsBetaCoordinatorBridgePresenterDelegate>
#pragma mark - bwi Messenger Additions
- (UITableViewCell*) cellForWorkTime:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_enable_rest_time", @"Vector", nil);
labelAndSwitchCell.mxkSwitch.on = [service isWorkTimeGlobalyEnabled];
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleRestService:) forControlEvents:UIControlEventTouchUpInside];
return labelAndSwitchCell;
}
- (void)toggleRestService:(UISwitch*)theSwitch
{
WorkTimeService *service = [WorkTimeService workTimeService:self.mainSession.myUser.userId];
[service enableWorkTime:theSwitch.isOn];
}
- (UITableViewCell*) cellForPersonalNotes:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath {
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];

View File

@@ -112,7 +112,7 @@ class NotificationService: UNNotificationServiceExtension {
setupAnalytics()
UNUserNotificationCenter.current().removeUnwantedNotifications()
// check if this is a Matrix notification
guard let roomId = userInfo["room_id"] as? String, let eventId = userInfo["event_id"] as? String else {
// it's not a Matrix notification, do not change the content
@@ -121,6 +121,14 @@ class NotificationService: UNNotificationServiceExtension {
return
}
// check if the event arrived outside the user defined working time for this room
guard !isRoomMuted(roomID: roomId) else {
// it's not a Matrix notification, do not change the content
MXLog.debug("[NotificationService] didReceiveRequest: This is not a Matrix notification.")
contentHandler(UNNotificationContent())
return
}
// save this content as fallback content
guard let content = request.content.mutableCopy() as? UNMutableNotificationContent else {
return
@@ -184,6 +192,44 @@ class NotificationService: UNNotificationServiceExtension {
}
}
private func isRoomMuted(roomID: String) -> Bool {
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
if #available(iOSApplicationExtension 14.0, *) {
guard let data = sharedUserDefaults?.data(forKey: NotificationTimes.sharedUserDefaultsKey), let notificationTimes = try? JSONDecoder().decode(NotificationTimes.self, from: data) else {
return false
}
let now = Date()
let calendar = Calendar.current
let weekday: NotificationTimesWeekday
switch calendar.component(.weekday, from: now) {
case 1: // sunday
weekday = notificationTimes.weekday(index: 6)
case 2: // monday
weekday = notificationTimes.weekday(index: 0)
case 3: // tuesday
weekday = notificationTimes.weekday(index: 1)
case 4: // wednesday
weekday = notificationTimes.weekday(index: 2)
case 5: // thursday
weekday = notificationTimes.weekday(index: 3)
case 6: // friday
weekday = notificationTimes.weekday(index: 4)
case 7: // saturday
weekday = notificationTimes.weekday(index: 5)
default:
MXLog.error("[NotificationService] isRoomMuted failed because of an invalid day component.")
return false
}
return notificationTimes.isEnabled && weekday.isEnabled && weekday.startTime <= now && weekday.endTime >= now
} else {
return false
}
}
private func setupAnalytics(){
// Configure our analytics. It will start if the option is enabled
let analytics = Analytics.shared

View File

@@ -10,5 +10,7 @@
<array>
<string>$(KEYCHAIN_ACCESS_GROUP)</string>
</array>
<key>com.apple.developer.usernotifications.filtering</key>
<true/>
</dict>
</plist>

View File

@@ -40,6 +40,10 @@ targets:
- path: ../bwi/AppConfig
- path: ../bwi/ServerURLs
- path: ../bwi/SecureStorage
- path: ../bwi/NotificationTimes/NotificationTimes.swift
- path: ../bwi/NotificationTimes/NotificationTimesRoomSettings.swift
- path: ../bwi/NotificationTimes/NotificationTimesWeekday.swift
- path: ../bwi/NotificationTimes/Date+fromHourMinute.swift
- path: ../bwi/BwiBuildSettings.swift
- path: ../Config/BwiSettings.swift
- path: ../Riot/Managers/Settings/RiotSettings.swift

View File

@@ -53,6 +53,8 @@ final class BwiBuildSettings: NSObject {
static let bwiSettingsShowSecureBackupSection = false
static let bwiSettingsShowCrossSigningSection = false
static let bwiNotificationTimes = true
static let bwiUserLabelsAdminSettingsVisible = true
static let bwiUserLabelsMemberDetailsVisible = true
static let bwiUserLabelsParticipantsVisible = true

View File

@@ -17,6 +17,7 @@
import SwiftUI
/// Helper class for making our SwiftUI view available to ObjectiveC
@objcMembers class DeveloperSettingsViewController: NSObject {
@@ -26,6 +27,8 @@ import SwiftUI
}
}
// MARK: -
@available(iOS 14.0, *)
struct DeveloperSettingsView: View {
let session: MXSession?

View File

@@ -1,5 +1,6 @@
//
/*
* Copyright (c) 2021 BWI GmbH
* Copyright (c) 2022 BWI GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +17,16 @@
import Foundation
struct WorkTimeInRoom : Codable, Hashable, Equatable {
var userId: String
var roomId: String
var isWorkTimeActive: Bool
func hash(into hasher: inout Hasher) {
hasher.combine(userId)
hasher.combine(roomId)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.roomId == rhs.roomId && lhs.userId == rhs.userId
extension Date {
static func from(hour: Int, minute: Int) -> Date {
let gregorianCalendar = NSCalendar(calendarIdentifier: .gregorian)!
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
let date = gregorianCalendar.date(from: dateComponents)!
return date
}
}

View File

@@ -0,0 +1,96 @@
//
/*
* Copyright (c) 2022 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 Foundation
import Combine
@available(iOS 14.0, *)
@objcMembers class NotificationTimes: NSObject, Codable {
static let shared = NotificationTimes()
static let sharedUserDefaultsKey = "notificationTimes"
var isEnabled: Bool
var monday, tuesday, wednesday, thursday, friday, saturday, sunday : NotificationTimesWeekday
var rooms: [String : Bool]
func weekday(index: Int) -> NotificationTimesWeekday {
switch index {
case 0:
return monday
case 1:
return tuesday
case 2:
return wednesday
case 3:
return thursday
case 4:
return friday
case 5:
return saturday
case 6:
return sunday
default:
assertionFailure("weekday index outside range 0...6")
return monday
}
}
override init() {
isEnabled = false
monday = NotificationTimesWeekday(id: 0,
name: NSLocalizedString("bwi_notification_times_monday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_monday_short", tableName: "Bwi", comment: ""))
tuesday = NotificationTimesWeekday(id: 1,
name: NSLocalizedString("bwi_notification_times_tuesday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_tuesday_short", tableName: "Bwi", comment: ""))
wednesday = NotificationTimesWeekday(id: 2,
name: NSLocalizedString("bwi_notification_times_wednesday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_wednesday_short", tableName: "Bwi", comment: ""))
thursday = NotificationTimesWeekday(id: 3,
name: NSLocalizedString("bwi_notification_times_thursday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_thursday_short", tableName: "Bwi", comment: ""))
friday = NotificationTimesWeekday(id: 4,
name: NSLocalizedString("bwi_notification_times_friday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_friday_short", tableName: "Bwi", comment: ""))
saturday = NotificationTimesWeekday(id: 5,
name: NSLocalizedString("bwi_notification_times_saturday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_saturday_short", tableName: "Bwi", comment: ""))
sunday = NotificationTimesWeekday(id: 6,
name: NSLocalizedString("bwi_notification_times_sunday", tableName: "Bwi", comment: ""),
shortName: NSLocalizedString("bwi_notification_times_sunday_short", tableName: "Bwi", comment: ""))
rooms = [:]
}
func isEnabledForRoom(roomID: String, isDirect: Bool) -> Bool {
if isDirect {
return rooms[roomID] ?? false
} else {
return rooms[roomID] ?? true
}
}
func enableForRoom(roomID: String) {
rooms[roomID] = true
}
func disableForRoom(roomID: String) {
rooms[roomID] = false
}
}

View File

@@ -1,5 +1,6 @@
//
/*
* Copyright (c) 2021 BWI GmbH
* Copyright (c) 2022 BWI GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +17,12 @@
import Foundation
struct WorkTimeUser : Codable {
let WorkTime : WorkTime
let userId : String
struct NotificationTimesRoomSettings: Codable {
var roomID: String
var enabled: Bool
private enum CodingKeys : String, CodingKey {
case roomID = "room_id"
case enabled
}
}

View File

@@ -0,0 +1,272 @@
//
/*
* Copyright (c) 2022 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
/// Helper class for making our SwiftUI view available to ObjectiveC
@objcMembers class NotificationTimesViewController: NSObject {
@available(iOS 14.0, *)
class func makeViewController(session: MXSession) -> UIViewController {
return UIHostingController(rootView: NotificationTimesView(session: session))
}
}
// MARK: -
@available(iOS 14.0, *)
struct NotificationTimesView: View {
@State var isOn = NotificationTimes.shared.isEnabled
@State var selectedDay: Int = 0
@State var weekdays = [NotificationTimes.shared.monday, NotificationTimes.shared.tuesday, NotificationTimes.shared.wednesday, NotificationTimes.shared.thursday, NotificationTimes.shared.friday, NotificationTimes.shared.saturday, NotificationTimes.shared.sunday]
var session: MXSession?
init(session: MXSession?) {
self.session = session
if let session = session {
RiotSharedSettings(session: session).fetchNotificationTimes()
}
}
var body: some View {
VStack(spacing: 50) {
NotificationTimesToggle(isOn: $isOn)
.padding(.top, 40)
if isOn {
HStack {
ForEach(weekdays) { weekday in
WeekDayButton(weekday: weekday, selectedDay: $selectedDay)
}
}
WeekDaysSettings(weekday: weekdays[selectedDay])
}
Spacer()
}
.padding()
.navigationTitle(NSLocalizedString("bwi_notification_times", tableName: "Bwi", comment: ""))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: updateAccountData) {
Text(NSLocalizedString("bwi_notification_times_done_action", tableName: "Bwi", comment: ""))
.foregroundColor(Color(ThemeService.shared().theme.tintColor))
}
}
}
.onDisappear {
updateAccountData()
updateSharedUserDefaults()
}
}
private func updateAccountData() {
if let session = session {
_ = RiotSharedSettings(session: session).storeNotificationTimes {
print("NotificationTimes successuflly stored to account data")
} failure: { error in
if let error = error {
print("Error: Storing NotificationTimes into account data failed")
print(error)
}
}
}
}
private func updateSharedUserDefaults() {
if let data = try? JSONEncoder().encode(NotificationTimes.shared) {
let sharedUserDefaults = MXKAppSettings.standard().sharedUserDefaults
sharedUserDefaults?.set(data, forKey: NotificationTimes.sharedUserDefaultsKey)
}
}
}
// MARK: -
@available(iOS 14.0, *)
fileprivate struct NotificationTimesToggle: View {
@Binding var isOn: Bool
var body: some View {
Toggle(isOn: $isOn) {
VStack(alignment: .leading, spacing: 8) {
Text(NSLocalizedString("bwi_notification_times_toggle_title", tableName: "Bwi", comment: ""))
.font(.headline)
.foregroundColor(.primary)
Text(NSLocalizedString("bwi_notification_times_toggle_description", tableName: "Bwi", comment: ""))
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.toggleStyle(SwitchToggleStyle(tint: Color(ThemeService.shared().theme.tintColor)))
}
}
// MARK: -
@available(iOS 14.0, *)
fileprivate struct WeekDayButton: View {
@ObservedObject var weekday: NotificationTimesWeekday
@Binding var selectedDay: Int
var body: some View {
Button(action: { selectedDay = weekday.id }) {
ZStack {
Group {
if weekday.isEnabled {
Circle()
.foregroundColor(Color(ThemeService.shared().theme.tintColor))
} else {
Circle()
.strokeBorder(Color.secondary, lineWidth: 1)
.background(Circle().fill(.clear))
}
Text(weekday.shortName)
.font(.subheadline)
.foregroundColor(.black)
}
.frame(width: 38, height: 38)
Circle()
.strokeBorder( weekday.id == selectedDay ? .gray : .clear, lineWidth: 2)
.frame(width: 45, height: 45)
}
}
}
}
// MARK: -
@available(iOS 14.0, *)
fileprivate struct WeekDaysSettings: View {
@ObservedObject var weekday: NotificationTimesWeekday
var body: some View {
VStack(spacing: 0) {
UserInfoText(weekdayName: weekday.name, notificationsEnabled: weekday.isEnabled)
Spacer()
.frame(height: 20)
HStack(spacing: 40) {
TimePicker(date: $weekday.startTime, title: NSLocalizedString("bwi_notification_times_start_time", tableName: "Bwi", comment: ""), enabled: weekday.isEnabled)
TimePicker(date: $weekday.endTime, title: NSLocalizedString("bwi_notification_times_end_time", tableName: "Bwi", comment: ""), enabled: weekday.isEnabled)
}
Spacer()
.frame(height: 50)
Button(action: { weekday.isEnabled.toggle() }) {
if weekday.isEnabled {
Text(String(format: NSLocalizedString("bwi_notification_times_button_deactivate", tableName: "Bwi", comment: ""), weekday.name))
.foregroundColor(Color(ThemeService.shared().theme.tintColor))
} else {
Text(String(format: NSLocalizedString("bwi_notification_times_button_activate", tableName: "Bwi", comment: ""), weekday.name))
.foregroundColor(Color(ThemeService.shared().theme.tintColor))
}
}
}
.onChange(of: weekday.startTime) { newValue in
if weekday.endTime < newValue {
weekday.endTime = newValue
}
}
.onChange(of: weekday.endTime) { newValue in
if weekday.startTime > newValue {
weekday.startTime = newValue
}
}
}
}
@available(iOS 14.0, *)
fileprivate struct UserInfoText: View {
var weekdayName: String
var notificationsEnabled: Bool
var body: some View {
if notificationsEnabled {
TwoLineText(String(format: NSLocalizedString("bwi_notification_times_status_activated", tableName: "Bwi", comment: ""), weekdayName), color: notificationsEnabled ? .primary : .secondary)
} else {
TwoLineText(String(format: NSLocalizedString("bwi_notification_times_status_deactivated", tableName: "Bwi", comment: ""), weekdayName), color: notificationsEnabled ? .primary : .secondary)
}
}
}
@available(iOS 14.0, *)
fileprivate struct TimePicker: View {
@Binding var date: Date
var title: String
var enabled: Bool
var body: some View {
VStack {
Text(title)
.foregroundColor(enabled ? .primary : .secondary)
ZStack {
DatePicker( "", selection: $date, displayedComponents: [.hourAndMinute])
.labelsHidden()
.opacity(enabled ? 1 : 0)
Text("--:--")
.foregroundColor(.secondary)
.opacity(enabled ? 0 : 1)
}
}
}
}
@available(iOS 14.0, *)
fileprivate struct TwoLineText: View {
var text: String
var color: Color
init(_ text: String, color: Color) {
self.text = text
self.color = color
}
var body: some View {
ZStack {
Text("X\nX")
.frame(maxWidth: .infinity)
.font(.body)
.foregroundColor(.clear)
Text(text)
.lineLimit(2)
.multilineTextAlignment(.center)
.font(.body)
.foregroundColor(color)
}
}
}
@available(iOS 14.0, *)
struct NotificationTimesView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
NotificationTimesView(session: nil)
.environment(\.locale, Locale.init(identifier: "de"))
}
}
}

View File

@@ -0,0 +1,74 @@
//
/*
* Copyright (c) 2022 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 Foundation
@available(iOS 14.0, *)
class NotificationTimesWeekday: ObservableObject, Identifiable, Codable {
@Published var isEnabled: Bool
@Published var name: String
@Published var shortName: String
@Published var startTime = Date.from(hour: 8, minute: 0)
@Published var endTime = Date.from(hour: 17, minute: 0)
let id: Int
private enum CodingKeys : String, CodingKey {
case fromHour = "from_hour"
case toHour = "to_hour"
case fromMinute = "from_minute"
case toMinute = "to_minute"
case isEnabled = "is_enabled"
}
init(id: Int, name: String, shortName: String, isEnabled: Bool = false) {
self.id = id
self.name = name
self.shortName = shortName
self.isEnabled = isEnabled
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let fromHour = try container.decode(Int.self, forKey: .fromHour)
let toHour = try container.decode(Int.self, forKey: .toHour)
let fromMinute = try container.decode(Int.self, forKey: .fromMinute)
let toMinute = try container.decode(Int.self, forKey: .toMinute)
isEnabled = try container.decode(Bool.self, forKey: .toMinute)
id = 0
name = ""
shortName = ""
startTime = Date.from(hour: fromHour, minute: fromMinute)
endTime = Date.from(hour: toHour, minute: toMinute)
}
func encode(to encoder: Encoder) throws {
let calender = Calendar.current
let fromHour = calender.component(.hour, from: startTime)
let toHour = calender.component(.hour, from: endTime)
let fromMinute = calender.component(.minute, from: startTime)
let toMinute = calender.component(.minute, from: endTime)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(isEnabled, forKey: .isEnabled)
try container.encode(fromHour, forKey: .fromHour)
try container.encode(toHour, forKey: .toHour)
try container.encode(fromMinute, forKey: .fromMinute)
try container.encode(toMinute, forKey: .toMinute)
}
}

View File

@@ -1,53 +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 Foundation
struct WorkTime : Codable {
enum Days : Int, CaseIterable, Codable {
case sunday = 1
case monday = 2
case tuesday = 3
case wednesday = 4
case thursday = 5
case friday = 6
case saturday = 7
case other
public init!(_ value:RawValue) {
guard let validValue = Days(rawValue:value) else {
self = .other
return
}
self = validValue
}
static func workdays() -> Set<Days> {
Set(Days.allCases.filter { (2...6).contains( $0.rawValue ) })
}
}
var workdays : Set<Days>
var startTime : Int
var finishTime : Int
init() {
workdays = WorkTime.Days.workdays()
startTime = 8 * 60
finishTime = 17 * 60
}
}

View File

@@ -1,43 +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 Foundation
@objcMembers class WorkTimeModel : NSObject {
func formatedWorkTime(_ workTime: WorkTime) -> String {
let now = Date()
let weekday = Calendar.current.component(.weekday, from: now)
if workTime.workdays.contains(WorkTime.Days(weekday)) {
let startHours = Int(workTime.startTime / 60)
let startMinutes = workTime.startTime % 60
let finishHours = Int(workTime.finishTime / 60)
let finishMinutes = workTime.finishTime % 60
let startTime = String(format: "%02d:%02d", startHours, startMinutes)
let finishTime = String(format: "%02d:%02d", finishHours, finishMinutes)
if workTime.startTime <= workTime.finishTime {
return String(format: NSLocalizedString("room_details_main_section_rest_time_time", tableName: "Vector", comment: ""), startTime, finishTime)
} else {
return String(format: NSLocalizedString("room_details_main_section_rest_time_time", tableName: "Vector", comment: ""), finishTime, startTime)
}
} else {
return NSLocalizedString("room_details_main_section_rest_time_restday", tableName: "Vector", comment: "")
}
}
}

View File

@@ -1,114 +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 Foundation
@objcMembers class WorkTimeService : NSObject {
static let minutesInDay = 60 * 24
var workTime : WorkTime
let store : WorkTimeStore
static func workTimeService(_ userId: String) -> WorkTimeService {
let store = WorkTimeStore(userId: userId)
return WorkTimeService(store)
}
init(_ store: WorkTimeStore) {
self.store = store
self.workTime = store.workTime
}
func setStartTime(_ startTime: Int) {
var fixedStartTime = startTime
if fixedStartTime < 0 {
fixedStartTime = 0
}
if fixedStartTime > WorkTimeService.minutesInDay {
fixedStartTime = WorkTimeService.minutesInDay
}
self.workTime.startTime = fixedStartTime
store.workTime = self.workTime
}
func setFinishTime(_ finishTime: Int) {
var fixedFinishTime = finishTime
if fixedFinishTime > WorkTimeService.minutesInDay {
fixedFinishTime = WorkTimeService.minutesInDay
}
if fixedFinishTime < 0 {
fixedFinishTime = 0
}
self.workTime.finishTime = fixedFinishTime
store.workTime = self.workTime
}
func addWorkDay(_ day: WorkTime.Days) {
self.workTime.workdays.insert(day)
store.workTime = self.workTime
}
func removeWorkDay(_ day: WorkTime.Days) {
self.workTime.workdays.remove(day)
store.workTime = self.workTime
}
func isNowWorkTime() -> Bool {
isWorkTime(Date())
}
func isWorkTime(_ date: Date) -> Bool {
let weekday = Calendar.current.component(.weekday, from: date)
if self.workTime.workdays.contains(WorkTime.Days(weekday)) {
let minutes = Calendar.current.component(.hour, from: date)*60 + Calendar.current.component(.minute, from: date)
if self.workTime.startTime <= self.workTime.finishTime {
if self.workTime.startTime <= minutes && self.workTime.finishTime >= minutes {
return true
}
} else {
if self.workTime.startTime <= minutes || self.workTime.finishTime >= minutes {
return true
}
}
}
return false
}
func isWorkTimeGlobalyEnabled() -> Bool {
return store.workTimeEnabled
}
func enableWorkTime(_ enable: Bool) {
store.workTimeEnabled = enable
}
func activateWorkTimeInRoom(_ roomId: String) {
self.store.activateWorkTimeInRoom(roomId);
}
func deactivateWorkTimeInRoom(_ roomId: String) {
self.store.deactivateWorkTimeInRoom(roomId)
}
func localizedWorkTime() -> String {
return WorkTimeModel().formatedWorkTime(store.workTime)
}
}

View File

@@ -1,147 +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 Foundation
import KeychainAccess
@objcMembers
final class WorkTimeStore : NSObject {
private enum StoreKeys {
static let WorkTimeEnabledKey = "WorkTimeEnabled"
static let WorkTimeKey = "WorkTime"
static let WorkTimeInRoomsKey = "WorkTimeInRooms"
}
private struct BwiSettingsConstants {
static let bwiSettingsKeychainService: String = BuildSettings.baseBundleIdentifier + ".bwi-WorkTime-service"
}
private let vault: KeyValueVault
private let userId: String
init(userId : String) {
self.userId = userId
vault = KeychainVault(Keychain(service: BwiSettingsConstants.bwiSettingsKeychainService,
accessGroup: BuildSettings.keychainAccessGroup))
super.init()
}
func reset() {
do {
try vault.removeObject(forKey: StoreKeys.WorkTimeKey)
try vault.removeObject(forKey: StoreKeys.WorkTimeEnabledKey)
try vault.removeObject(forKey: StoreKeys.WorkTimeInRoomsKey)
} catch let error {
NSLog("[WorkTime] Error when removing objects: \(error)")
}
}
// MARK: Servers
var workTimeEnabled : Bool {
get {
do {
return try vault.bool(forKey: StoreKeys.WorkTimeEnabledKey) ?? false
} catch {
return false
}
}
set {
do {
try vault.set(newValue, forKey: StoreKeys.WorkTimeEnabledKey)
} catch let error {
NSLog("[WorkTime] Error when storing rest time enabled to the vault: \(error)")
}
}
}
var workTime : WorkTime {
get {
do {
guard let data = try vault.data(forKey: StoreKeys.WorkTimeKey) else {
return WorkTime()
}
return try JSONDecoder().decode(WorkTimeUser.self, from: data).WorkTime
} catch {
return WorkTime()
}
}
set {
do {
let userWorkTime = WorkTimeUser(WorkTime: newValue, userId: userId)
let data = try JSONEncoder().encode(userWorkTime)
try vault.set(data, forKey: StoreKeys.WorkTimeKey)
} catch let error {
NSLog("[WorkTime] Error when storing addional header to the vault: \(error)")
}
}
}
func activateWorkTimeInRoom(_ roomId: String) {
let workTimeInRoom = WorkTimeInRoom(userId: userId, roomId: roomId, isWorkTimeActive: true)
var roomSet = roomSetFromStore()
roomSet.update(with: workTimeInRoom)
saveRoomSetToStore(roomSet)
}
func deactivateWorkTimeInRoom(_ roomId: String) {
let workTimeInRoom = WorkTimeInRoom(userId: userId, roomId: roomId, isWorkTimeActive: false)
var roomSet = roomSetFromStore()
roomSet.update(with: workTimeInRoom)
saveRoomSetToStore(roomSet)
}
@objc func isWorkTimeRoom(_ roomId: String) -> Bool {
let roomSet = roomSetFromStore()
if roomSet.first(where: {$0.roomId == roomId && $0.userId == userId}) != nil {
return true
}
return false
}
@objc func isWorkTimeInRoomActive(_ roomId: String) -> Bool {
let roomSet = roomSetFromStore()
if let WorkTimeInRoom = roomSet.first(where: {$0.roomId == roomId && $0.userId == userId}) {
return WorkTimeInRoom.isWorkTimeActive
}
return false
}
private func roomSetFromStore() -> Set<WorkTimeInRoom> {
do {
guard let data = try vault.data(forKey: StoreKeys.WorkTimeInRoomsKey) else {
return Set<WorkTimeInRoom>()
}
return try JSONDecoder().decode(Set<WorkTimeInRoom>.self, from: data)
} catch {
return Set<WorkTimeInRoom>()
}
}
private func saveRoomSetToStore(_ roomSet: Set<WorkTimeInRoom>) {
do {
print("saveRoomSetToStore", roomSet)
let dataOut = try JSONEncoder().encode(roomSet)
try vault.set(dataOut, forKey: StoreKeys.WorkTimeInRoomsKey)
} catch {
}
}
}