mirror of
https://github.com/twofas/2fas-ios.git
synced 2024-11-22 02:10:19 +01:00
Merge pull request #129 from twofas/feature/TF-1552
[TF-1552] Notification animation and change notifications order
This commit is contained in:
commit
2712335a60
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "NavibarNewsIconBadge.pdf",
|
||||
"filename" : "Ellipse 10.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
71
TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf
vendored
Normal file
71
TwoFAS/TwoFAS/Other/Assets.xcassets/Badge.imageset/Ellipse 10.pdf
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
0.929412 0.109804 0.141176 scn
|
||||
3.000000 1.500000 m
|
||||
3.000000 0.671573 2.328427 0.000000 1.500000 0.000000 c
|
||||
0.671573 0.000000 0.000000 0.671573 0.000000 1.500000 c
|
||||
0.000000 2.328427 0.671573 3.000000 1.500000 3.000000 c
|
||||
2.328427 3.000000 3.000000 2.328427 3.000000 1.500000 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
371
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 3.000000 3.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000461 00000 n
|
||||
0000000483 00000 n
|
||||
0000000654 00000 n
|
||||
0000000728 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
787
|
||||
%%EOF
|
@ -8,8 +8,5 @@
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -34,6 +34,7 @@ internal enum Asset {
|
||||
internal static let deleteSettingsIcon = ImageAsset(name: "DeleteSettingsIcon")
|
||||
internal static let backupDeleted = ImageAsset(name: "backupDeleted")
|
||||
internal static let backupSettingsIcon = ImageAsset(name: "backupSettingsIcon")
|
||||
internal static let badge = ImageAsset(name: "Badge")
|
||||
internal static let barsBackground = ImageAsset(name: "BarsBackground")
|
||||
internal static let bracket = ImageAsset(name: "Bracket")
|
||||
internal static let aboutExtension = ImageAsset(name: "AboutExtension")
|
||||
@ -110,7 +111,6 @@ internal enum Asset {
|
||||
internal static let naviIconAddFirst = ImageAsset(name: "NaviIconAddFirst")
|
||||
internal static let naviSortIcon = ImageAsset(name: "NaviSortIcon")
|
||||
internal static let navibarNewsIcon = ImageAsset(name: "NavibarNewsIcon")
|
||||
internal static let navibarNewsIconBadge = ImageAsset(name: "NavibarNewsIconBadge")
|
||||
internal static let notificationFeatures = ImageAsset(name: "NotificationFeatures")
|
||||
internal static let notificationNews = ImageAsset(name: "NotificationNews")
|
||||
internal static let notificationTips = ImageAsset(name: "NotificationTips")
|
||||
|
@ -91,7 +91,8 @@ private extension NewsPresenter {
|
||||
func reload() {
|
||||
let now = Date()
|
||||
interactor.fetchList { [weak self] news in
|
||||
let cells = news.map { entry in
|
||||
let sortedNews = news.sorted { $0.publishedAt > $1.publishedAt }
|
||||
var cells = sortedNews.map { entry in
|
||||
NewsCell(
|
||||
icon: entry.icon.image,
|
||||
title: entry.message ?? entry.link?.absoluteString ?? "",
|
||||
|
@ -531,7 +531,7 @@ private extension TokensPresenter {
|
||||
}()
|
||||
|
||||
if interactor.hasServices {
|
||||
updateAddServiceIcon()
|
||||
updateNaviIcons()
|
||||
view?.showList()
|
||||
|
||||
if Set<CategoryData>(currentServices) != Set<CategoryData>(newServices) || changeRequriesTokenRefresh {
|
||||
@ -552,7 +552,7 @@ private extension TokensPresenter {
|
||||
if !isSearching && currentState == .edit {
|
||||
setCurrentState(.normal)
|
||||
}
|
||||
updateAddServiceIcon()
|
||||
updateNaviIcons()
|
||||
interactor.stopCounters()
|
||||
updateEditStateButton()
|
||||
|
||||
@ -595,17 +595,23 @@ private extension TokensPresenter {
|
||||
}
|
||||
|
||||
func updateNewsIcon() {
|
||||
updateAddServiceIcon()
|
||||
updateNaviIcons()
|
||||
interactor.fetchNews { [weak self] in
|
||||
self?.updateAddServiceIcon()
|
||||
self?.updateNaviIcons(hasUnreadNews: self?.hasUnreadNews ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAddServiceIcon() {
|
||||
private func updateNaviIcons(hasUnreadNews: Bool = false) {
|
||||
if interactor.hasServices {
|
||||
view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: false))
|
||||
view?.updateNaviIcons(
|
||||
using: mapButtonStateFor(currentState, isFirst: false),
|
||||
hasUnreadNews: hasUnreadNews
|
||||
)
|
||||
} else {
|
||||
view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: !isSearching))
|
||||
view?.updateNaviIcons(
|
||||
using: mapButtonStateFor(currentState, isFirst: !isSearching),
|
||||
hasUnreadNews: hasUnreadNews
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ protocol TokensViewControlling: AnyObject {
|
||||
func enableDragging()
|
||||
func disableDragging()
|
||||
|
||||
func updateAddIcon(using state: TokensViewControllerAddState)
|
||||
func updateNaviIcons(using state: TokensViewControllerAddState, hasUnreadNews: Bool)
|
||||
func updateEditState(using state: TokensViewControllerEditState)
|
||||
|
||||
func lockBars()
|
||||
@ -125,22 +125,25 @@ extension TokensViewController: TokensViewControlling {
|
||||
func disableDragging() {
|
||||
tokensView.dragInteractionEnabled = false
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Navibar icons
|
||||
func updateAddIcon(using state: TokensViewControllerAddState) {
|
||||
func createNewsIcon() -> UIBarButtonItem {
|
||||
let img: UIImage = {
|
||||
presenter.hasUnreadNews ? Asset.navibarNewsIconBadge.image : Asset.navibarNewsIcon.image
|
||||
}()
|
||||
img.withTintColor(Theme.Colors.Icon.theme)
|
||||
let button = UIBarButtonItem(
|
||||
image: img,
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(showNotifications)
|
||||
)
|
||||
button.accessibilityLabel = T.Commons.notifications
|
||||
return button
|
||||
func updateNaviIcons(using state: TokensViewControllerAddState, hasUnreadNews: Bool) {
|
||||
func createNewsButton() -> UIBarButtonItem {
|
||||
if presenter.hasUnreadNews {
|
||||
let naviButton = UnreadNewsNaviButton()
|
||||
naviButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
naviButton.accessibilityLabel = T.Commons.notifications
|
||||
naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside)
|
||||
naviButton.animate()
|
||||
return UIBarButtonItem(customView: naviButton)
|
||||
} else {
|
||||
let naviButton = UIButton(type: .custom)
|
||||
naviButton.setBackgroundImage(Asset.navibarNewsIcon.image, for: .normal)
|
||||
naviButton.addTarget(self, action: #selector(showNotifications), for: .touchUpInside)
|
||||
naviButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
naviButton.accessibilityLabel = T.Commons.notifications
|
||||
return UIBarButtonItem(customView: naviButton)
|
||||
}
|
||||
}
|
||||
|
||||
func createAddButton(image: UIImage) -> UIBarButtonItem {
|
||||
@ -156,15 +159,33 @@ extension TokensViewController: TokensViewControlling {
|
||||
|
||||
switch state {
|
||||
case .firstTime:
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAddFirst.image),
|
||||
createNewsIcon()
|
||||
]
|
||||
if let newsButton, hasUnreadNews {
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAddFirst.image),
|
||||
newsButton
|
||||
]
|
||||
} else {
|
||||
let newsButton = createNewsButton()
|
||||
self.newsButton = newsButton
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAddFirst.image),
|
||||
newsButton
|
||||
]
|
||||
}
|
||||
case .normal:
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAdd.image),
|
||||
createNewsIcon()
|
||||
]
|
||||
if let newsButton, !hasUnreadNews {
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAdd.image),
|
||||
newsButton
|
||||
]
|
||||
} else {
|
||||
let newsButton = createNewsButton()
|
||||
self.newsButton = newsButton
|
||||
navigationItem.rightBarButtonItems = [
|
||||
createAddButton(image: Asset.naviIconAdd.image),
|
||||
newsButton
|
||||
]
|
||||
}
|
||||
case .none:
|
||||
let buttonSection = UIBarButtonItem(
|
||||
image: Asset.addCategory.image,
|
||||
@ -354,3 +375,88 @@ extension TokensViewController {
|
||||
presenter.handleAppUnlocked()
|
||||
}
|
||||
}
|
||||
|
||||
private extension TokensViewController {
|
||||
final class UnreadNewsNaviButton: UIButton {
|
||||
let newsImageView = UIImageView(image: Asset.navibarNewsIcon.image)
|
||||
let badgeImageView = UIImageView(image: Asset.badge.image)
|
||||
|
||||
private let badgeWidth: CGFloat = 3
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupViews()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setupViews()
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
newsImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(newsImageView)
|
||||
|
||||
badgeImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(badgeImageView)
|
||||
badgeImageView.isHidden = true
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
newsImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
newsImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
badgeImageView.topAnchor.constraint(equalTo: topAnchor, constant: Theme.Metrics.halfSpacing),
|
||||
badgeImageView.trailingAnchor.constraint(
|
||||
equalTo: trailingAnchor,
|
||||
constant: -Theme.Metrics.quaterSpacing
|
||||
),
|
||||
badgeImageView.widthAnchor.constraint(equalToConstant: badgeWidth),
|
||||
badgeImageView.heightAnchor.constraint(equalToConstant: badgeWidth)
|
||||
])
|
||||
}
|
||||
|
||||
func animate() {
|
||||
let angle: Double = .pi / 12
|
||||
let numberOfFrames: Double = 5
|
||||
let frameDuration = Double(0.7 / numberOfFrames)
|
||||
|
||||
UIView.animateKeyframes(
|
||||
withDuration: 1,
|
||||
delay: 0,
|
||||
animations: { [newsImageView] in
|
||||
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: frameDuration) {
|
||||
newsImageView.transform = CGAffineTransform(rotationAngle: -angle)
|
||||
}
|
||||
UIView.addKeyframe(withRelativeStartTime: frameDuration, relativeDuration: frameDuration) {
|
||||
newsImageView.transform = CGAffineTransform(rotationAngle: +angle)
|
||||
}
|
||||
UIView.addKeyframe(withRelativeStartTime: 2 * frameDuration, relativeDuration: frameDuration) {
|
||||
newsImageView.transform = CGAffineTransform(rotationAngle: -angle)
|
||||
}
|
||||
UIView.addKeyframe(withRelativeStartTime: 3 * frameDuration, relativeDuration: frameDuration) {
|
||||
newsImageView.transform = CGAffineTransform(rotationAngle: +angle)
|
||||
}
|
||||
UIView.addKeyframe(withRelativeStartTime: 4 * frameDuration, relativeDuration: frameDuration) {
|
||||
newsImageView.transform = CGAffineTransform.identity
|
||||
}
|
||||
},
|
||||
completion: { [weak self] _ in
|
||||
self?.badgeImageView.isHidden = false
|
||||
self?.animateBadge()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private func animateBadge() {
|
||||
UIView.animate(
|
||||
withDuration: 0.2,
|
||||
animations: { [badgeImageView, badgeWidth] in
|
||||
badgeImageView.transform = CGAffineTransform(scaleX: 12.0 / badgeWidth, y: 12.0 / badgeWidth)
|
||||
}, completion: { [badgeImageView, badgeWidth] _ in
|
||||
UIView.animate(withDuration: 0.15) {
|
||||
badgeImageView.transform = CGAffineTransform(scaleX: 8.0 / badgeWidth, y: 8.0 / badgeWidth)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ final class TokensViewController: UIViewController {
|
||||
var addButton: UIBarButtonItem? {
|
||||
navigationItem.rightBarButtonItem
|
||||
}
|
||||
var newsButton: UIBarButtonItem?
|
||||
|
||||
private(set) var tokensView: TokensView!
|
||||
private(set) var dataSource: UICollectionViewDiffableDataSource<TokensSection, TokenCell>!
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user