Change license to AGPL
This commit:
- Updates the `LICENSE` file
- Start every file with something like:
// Copyright YEAR_FIRST_PUBLISHED Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
---
First, I removed existing license headers with this Ruby 3.1.2 script:
require 'set'
EXTENSIONS_TO_CHECK = Set['.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift']
same = 0
different = 0
all_files = `git ls-files`.lines.map { |line| line.strip }
all_files.each do |relative_path|
if relative_path == 'Pods'
next
end
unless EXTENSIONS_TO_CHECK.include? File.extname(relative_path)
next
end
path = File.expand_path(relative_path)
contents = File.read(path)
new_contents = contents.sub(/\/\/\n\/\/ Copyright .*\n\/\/\n\n/, '')
if contents == new_contents
same += 1
else
different += 1
end
File.write(path, new_contents)
end
puts "updated #{different} file(s), left #{same} untouched"
I'm sure this script could be improved, but it worked well enough.
Then, I created `Scripts/lint/lint-license-headers` and ran it to auto-
fix a lot of files. This changed the mode of some files, but I think
that's actually desirable. For example,
`SignalServiceKit/src/Util/AppContext.m` previously had a mode of
`0755/-rwxr-xr-x`, and it's now `0644/-rw-r--r--`.
Then I fixed some stragglers and updated the precommit script.
See [a similar change in the Desktop app][0].
[0]: 8bfaf598af
198 lines
7.4 KiB
Swift
198 lines
7.4 KiB
Swift
//
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class ExpandableContactListView: UIView {
|
|
|
|
var contactNames: [String] = [] {
|
|
didSet {
|
|
guard #available(iOS 13, *) else {
|
|
textLabel.text = contactNames.joined(separator: ", ")
|
|
return
|
|
}
|
|
textLabel.text = ListFormatter().string(from: contactNames)
|
|
}
|
|
}
|
|
|
|
var expanded: Bool = false {
|
|
didSet {
|
|
scrollView.isScrollEnabled = expanded
|
|
scrollViewMaxWidthConstraint?.isActive = !expanded
|
|
if !expanded {
|
|
scrollView.contentOffset = .zero
|
|
}
|
|
}
|
|
}
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
|
|
textLabel.textColor = tintColor
|
|
|
|
let pillView = PillView()
|
|
pillView.layoutMargins = UIEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 0)
|
|
pillView.autoSetDimension(.height, toSize: RoundMediaButton.visibleButtonSize)
|
|
addSubview(pillView)
|
|
pillView.autoPinEdgesToSuperviewEdges()
|
|
|
|
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
|
pillView.addSubview(backgroundView)
|
|
backgroundView.autoPinEdgesToSuperviewEdges()
|
|
|
|
let arrowView = UIImageView(image: #imageLiteral(resourceName: "arrow-right-16"))
|
|
pillView.addSubview(arrowView)
|
|
arrowView.autoPinEdge(toSuperviewMargin: .leading)
|
|
arrowView.autoVCenterInSuperview()
|
|
|
|
scrollViewContainer.clipsToBounds = true
|
|
scrollViewContainer.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: ExpandableContactListView.gradientWidth)
|
|
pillView.addSubview(scrollViewContainer)
|
|
scrollViewContainer.autoPinEdges(toSuperviewMarginsExcludingEdge: .leading)
|
|
scrollViewContainer.leadingAnchor.constraint(equalTo: arrowView.trailingAnchor, constant: 4).isActive = true
|
|
|
|
scrollView.delegate = self
|
|
scrollView.clipsToBounds = false
|
|
scrollView.isScrollEnabled = expanded
|
|
scrollViewContainer.addSubview(scrollView)
|
|
scrollView.autoPinEdgesToSuperviewMargins()
|
|
|
|
scrollView.addSubview(textLabel)
|
|
textLabel.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
|
|
textLabel.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
|
|
textLabel.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
|
|
textLabel.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor).isActive = true
|
|
scrollView.heightAnchor.constraint(equalTo: textLabel.heightAnchor).isActive = true
|
|
|
|
// This constraint sets intrinsic content width on the scroll view.
|
|
addConstraint({
|
|
let constraint = scrollView.widthAnchor.constraint(equalTo: textLabel.widthAnchor)
|
|
constraint.priority = .defaultLow
|
|
return constraint
|
|
}())
|
|
|
|
// Limit scroll view width in expanded state to 128 pts.
|
|
let scrollViewMaxWidthConstraint = scrollViewContainer.widthAnchor.constraint(lessThanOrEqualToConstant: 128)
|
|
if !expanded {
|
|
addConstraint(scrollViewMaxWidthConstraint)
|
|
}
|
|
self.scrollViewMaxWidthConstraint = scrollViewMaxWidthConstraint
|
|
|
|
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(gestureRecognizer:))))
|
|
}
|
|
|
|
@available(*, unavailable, message: "Use init(frame:)")
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func tintColorDidChange() {
|
|
super.tintColorDidChange()
|
|
textLabel.textColor = tintColor
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
DispatchQueue.main.async {
|
|
self.updateTextLabelEdgesFading()
|
|
}
|
|
}
|
|
|
|
// MARK: - Layout
|
|
|
|
private let scrollViewContainer = UIView()
|
|
|
|
private let scrollView: UIScrollView = {
|
|
let scrollView = UIScrollView()
|
|
scrollView.showsVerticalScrollIndicator = false
|
|
scrollView.showsHorizontalScrollIndicator = false
|
|
scrollView.alwaysBounceHorizontal = false
|
|
return scrollView
|
|
}()
|
|
|
|
private let textLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.numberOfLines = 1
|
|
label.lineBreakMode = .byClipping
|
|
label.font = .ows_dynamicTypeBody2Clamped
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
return label
|
|
}()
|
|
|
|
private var scrollViewMaxWidthConstraint: NSLayoutConstraint?
|
|
static private let gradientWidth: CGFloat = 14
|
|
private var isLeadingEdgeFaded = false
|
|
private var isTrailingEdgeFaded = false
|
|
|
|
private func updateTextLabelEdgesFading() {
|
|
|
|
// This method would be called in a tight loop when users scrolls.
|
|
// Therefore only re-create mask layer if it is necessary.
|
|
let shouldFadeLeading = scrollView.contentOffset.x > 0
|
|
let shouldFadeTrailing = scrollView.contentOffset.x < scrollView.contentSize.width - scrollView.frame.width
|
|
var shouldUpdateLayerMask = shouldFadeLeading != isLeadingEdgeFaded || shouldFadeTrailing != isTrailingEdgeFaded
|
|
|
|
// Mask layer doesn't resize automatically and therefore width change
|
|
// (switching to/from expanded state) mandates mask update.
|
|
if !shouldUpdateLayerMask, let maskLayer = scrollViewContainer.layer.mask {
|
|
shouldUpdateLayerMask = maskLayer.bounds.width != scrollViewContainer.width
|
|
}
|
|
|
|
guard shouldUpdateLayerMask else {
|
|
return
|
|
}
|
|
|
|
isLeadingEdgeFaded = shouldFadeLeading
|
|
isTrailingEdgeFaded = shouldFadeTrailing
|
|
|
|
// Simplest case: no edge fading - no mask layer.
|
|
guard isLeadingEdgeFaded || isTrailingEdgeFaded else {
|
|
scrollViewContainer.layer.mask = nil
|
|
return
|
|
}
|
|
|
|
let gradientWidthInPercent = Self.gradientWidth / scrollViewContainer.width
|
|
|
|
let gradientStopLocations: [CGFloat] = [ 0, gradientWidthInPercent, 1-gradientWidthInPercent, 1 ]
|
|
var gradientColors: [UIColor] = [ .black, .black ]
|
|
gradientColors.insert(isLeadingEdgeFaded ? .clear : .black, at: 0)
|
|
gradientColors.append(isTrailingEdgeFaded ? .clear : .black)
|
|
|
|
let gradientLayer = CAGradientLayer()
|
|
gradientLayer.frame = scrollViewContainer.bounds
|
|
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
|
|
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
|
|
gradientLayer.colors = gradientColors.map { $0.cgColor }
|
|
gradientLayer.locations = gradientStopLocations.map { NSNumber(value: $0) }
|
|
scrollViewContainer.layer.mask = gradientLayer
|
|
}
|
|
}
|
|
|
|
extension ExpandableContactListView {
|
|
|
|
@objc
|
|
private func handleSingleTap(gestureRecognizer: UITapGestureRecognizer) {
|
|
expanded = !expanded
|
|
UIView.animate(withDuration: 0.3,
|
|
animations: {
|
|
self.superview?.setNeedsLayout()
|
|
self.superview?.layoutIfNeeded()
|
|
if self.expanded {
|
|
self.updateTextLabelEdgesFading()
|
|
}
|
|
},
|
|
completion: { _ in
|
|
self.updateTextLabelEdgesFading()
|
|
})
|
|
}
|
|
}
|
|
|
|
extension ExpandableContactListView: UIScrollViewDelegate {
|
|
|
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
updateTextLabelEdgesFading()
|
|
}
|
|
}
|