mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
## Summary - add iOS and Android voice-note recording and preview directly in the mobile composer - add waveform playback, scrubbing, speed controls, haptics, and one-shot playback in chat - package recordings in a canonical H.264/AAC MP4 envelope on both platforms so existing relays accept them - preserve the shared composer interaction and attachment-card treatment across mobile platforms Mobile counterpart to #6978. ## Testing - `just ci` - `just mobile-check` - `just mobile-test` (2,026 tests) - Android debug build compiled, installed, launched, and Voice note verified in the attachment menu on Pixel 10 - signed iOS device build installed on iPhone --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
143 lines
4.3 KiB
Swift
143 lines
4.3 KiB
Swift
import Flutter
|
|
import UIKit
|
|
|
|
@available(iOS 26.0, *)
|
|
extension NativeAttachmentPopoverViewController {
|
|
func makeMenuView() -> UIView {
|
|
let container = UIView()
|
|
container.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
let scrollView = UIScrollView()
|
|
scrollView.alwaysBounceVertical = false
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
container.addSubview(scrollView)
|
|
|
|
let stack = UIStackView()
|
|
stack.axis = .vertical
|
|
stack.distribution = .fillEqually
|
|
stack.spacing = NativeAttachmentMenuLayout.itemSpacing
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
scrollView.addSubview(stack)
|
|
let stackHeightConstraint = stack.heightAnchor.constraint(
|
|
equalToConstant: NativeAttachmentMenuLayout.itemsHeight(
|
|
compatibleWith: traitCollection
|
|
)
|
|
)
|
|
menuStackHeightConstraint = stackHeightConstraint
|
|
NSLayoutConstraint.activate([
|
|
scrollView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
|
scrollView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
|
scrollView.topAnchor.constraint(equalTo: container.topAnchor),
|
|
scrollView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
|
stack.leadingAnchor.constraint(
|
|
equalTo: scrollView.frameLayoutGuide.leadingAnchor,
|
|
constant: NativeAttachmentMenuLayout.contentPadding
|
|
),
|
|
stack.trailingAnchor.constraint(
|
|
equalTo: scrollView.frameLayoutGuide.trailingAnchor,
|
|
constant: -NativeAttachmentMenuLayout.contentPadding
|
|
),
|
|
stack.topAnchor.constraint(
|
|
equalTo: scrollView.contentLayoutGuide.topAnchor,
|
|
constant: NativeAttachmentMenuLayout.contentPadding
|
|
),
|
|
stack.bottomAnchor.constraint(
|
|
equalTo: scrollView.contentLayoutGuide.bottomAnchor,
|
|
constant: -NativeAttachmentMenuLayout.contentPadding
|
|
),
|
|
stackHeightConstraint,
|
|
])
|
|
|
|
stack.addArrangedSubview(
|
|
makeNativeAttachmentMenuButton(
|
|
title: "Camera",
|
|
symbol: "camera",
|
|
action: { [weak self] in self?.showCamera() }
|
|
)
|
|
)
|
|
stack.addArrangedSubview(
|
|
makeNativeAttachmentMenuButton(
|
|
title: "Photos",
|
|
symbol: "photo.on.rectangle.angled",
|
|
action: { [weak self] in self?.showPhotos() }
|
|
)
|
|
)
|
|
stack.addArrangedSubview(
|
|
makeNativeAttachmentMenuButton(
|
|
title: "Video",
|
|
symbol: "video",
|
|
action: { [weak self] in
|
|
self?.finish(method: "pickVideo")
|
|
}
|
|
)
|
|
)
|
|
stack.addArrangedSubview(
|
|
makeNativeAttachmentMenuButton(
|
|
title: "Voice note",
|
|
symbol: "mic",
|
|
action: { [weak self] in
|
|
self?.finish(
|
|
method: "recordVoiceNote",
|
|
notifyBeforeDismissal: true
|
|
)
|
|
}
|
|
)
|
|
)
|
|
stack.addArrangedSubview(
|
|
makeNativeAttachmentMenuButton(
|
|
title: "Files",
|
|
symbol: "doc",
|
|
action: { [weak self] in
|
|
self?.finish(method: "pickFiles")
|
|
}
|
|
)
|
|
)
|
|
return container
|
|
}
|
|
|
|
func makeGlassControl(
|
|
title: String?,
|
|
symbol: String?,
|
|
accessibilityLabel: String,
|
|
prominent: Bool = false,
|
|
action: @escaping () -> Void
|
|
) -> UIButton {
|
|
var configuration =
|
|
prominent
|
|
? UIButton.Configuration.prominentGlass()
|
|
: UIButton.Configuration.glass()
|
|
configuration.title = title
|
|
if prominent {
|
|
configuration.baseBackgroundColor = .black
|
|
}
|
|
if let symbol {
|
|
configuration.image = UIImage(systemName: symbol)
|
|
}
|
|
configuration.imagePadding = 8
|
|
configuration.baseForegroundColor = .white
|
|
configuration.titleTextAttributesTransformer =
|
|
UIConfigurationTextAttributesTransformer { attributes in
|
|
var interAttributes = attributes
|
|
interAttributes.font = NativeAttachmentMenuTypography.font(
|
|
forTextStyle: .body
|
|
)
|
|
return interAttributes
|
|
}
|
|
configuration.contentInsets = NSDirectionalEdgeInsets(
|
|
top: 11,
|
|
leading: 15,
|
|
bottom: 11,
|
|
trailing: 15
|
|
)
|
|
let button = UIButton(
|
|
configuration: configuration,
|
|
primaryAction: UIAction { _ in
|
|
UISelectionFeedbackGenerator().selectionChanged()
|
|
action()
|
|
}
|
|
)
|
|
button.accessibilityLabel = accessibilityLabel
|
|
return button
|
|
}
|
|
}
|