mirror of
https://github.com/block/buzz.git
synced 2026-11-05 00:41:28 +00:00
## Summary Adds Buzzies and refreshes the agent avatar picker with larger previews, rounded option strips, and clearer category choices. Also moves Agents above Appearance in the home menu and adds a custom agent icon. ### Related issue None found. ### Testing Analysis, 4,138 app tests, package tests, and 121 configured-auth tests pass. Earlier visual build exercised on Pixel and iOS simulator, with iPhone review; snapshots below show that build. | Android · Pixel | iOS · simulator preview | | --- | --- | |  |  | --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
219 lines
6.7 KiB
Swift
219 lines
6.7 KiB
Swift
import Flutter
|
|
import UIKit
|
|
|
|
final class ConcentricSheetSurfaceFactory: NSObject, FlutterPlatformViewFactory {
|
|
private let messenger: FlutterBinaryMessenger
|
|
|
|
init(messenger: FlutterBinaryMessenger) {
|
|
self.messenger = messenger
|
|
super.init()
|
|
}
|
|
|
|
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
|
|
FlutterStandardMessageCodec.sharedInstance()
|
|
}
|
|
|
|
func create(
|
|
withFrame frame: CGRect,
|
|
viewIdentifier viewId: Int64,
|
|
arguments args: Any?
|
|
) -> FlutterPlatformView {
|
|
ConcentricSheetSurfacePlatformView(
|
|
frame: frame,
|
|
viewIdentifier: viewId,
|
|
messenger: messenger,
|
|
arguments: args
|
|
)
|
|
}
|
|
}
|
|
|
|
final class ConcentricSheetSurfacePlatformView: NSObject, FlutterPlatformView {
|
|
private let rootView: UIView
|
|
private let surfaceView: UIView
|
|
private let channel: FlutterMethodChannel
|
|
private let corners: String
|
|
private let usesGlass: Bool
|
|
private let topRadius: Double?
|
|
|
|
init(
|
|
frame: CGRect,
|
|
viewIdentifier viewId: Int64,
|
|
messenger: FlutterBinaryMessenger,
|
|
arguments args: Any?
|
|
) {
|
|
let arguments = args as? [String: Any]
|
|
let colorValue = (arguments?["color"] as? NSNumber)?.uint32Value ?? 0xFFFF_FFFF
|
|
let backdropColorValue = (arguments?["backdropColor"] as? NSNumber)?.uint32Value
|
|
let minimumRadius = (arguments?["minimumRadius"] as? NSNumber)?.doubleValue ?? 24
|
|
corners = arguments?["corners"] as? String ?? "all"
|
|
topRadius = (arguments?["topRadius"] as? NSNumber)?.doubleValue
|
|
usesGlass = (arguments?["usesGlass"] as? NSNumber)?.boolValue == true
|
|
|
|
rootView = UIView(frame: frame)
|
|
rootView.isUserInteractionEnabled = false
|
|
rootView.isOpaque = !usesGlass && backdropColorValue != nil
|
|
rootView.backgroundColor = backdropColorValue.map { Self.color(from: $0) } ?? .clear
|
|
|
|
if #available(iOS 26.0, *), usesGlass {
|
|
let effect = UIGlassEffect(style: .regular)
|
|
effect.isInteractive = false
|
|
surfaceView = UIVisualEffectView(effect: effect)
|
|
} else {
|
|
surfaceView = UIView()
|
|
}
|
|
surfaceView.frame = rootView.bounds
|
|
surfaceView.isUserInteractionEnabled = false
|
|
surfaceView.isOpaque = !usesGlass
|
|
surfaceView.clipsToBounds = true
|
|
surfaceView.layer.cornerCurve = .continuous
|
|
surfaceView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
rootView.addSubview(surfaceView)
|
|
|
|
channel = FlutterMethodChannel(
|
|
name: "buzz/concentric_sheet_surface/\(viewId)",
|
|
binaryMessenger: messenger
|
|
)
|
|
|
|
super.init()
|
|
|
|
updateColors(
|
|
colorValue: colorValue,
|
|
backdropColorValue: backdropColorValue,
|
|
glassTintColorValue: (arguments?["glassTintColor"] as? NSNumber)?.uint32Value
|
|
)
|
|
applyInterfaceStyle(from: arguments?["brightness"])
|
|
applyCornerConfiguration(minimumRadius: minimumRadius, capsule: arguments?["capsule"] as? Bool == true)
|
|
|
|
channel.setMethodCallHandler { [weak self] call, result in
|
|
guard let self else {
|
|
result(nil)
|
|
return
|
|
}
|
|
switch call.method {
|
|
case "updateColors":
|
|
guard
|
|
let arguments = call.arguments as? [String: Any],
|
|
let colorValue = arguments["color"] as? NSNumber
|
|
else {
|
|
result(
|
|
FlutterError(
|
|
code: "invalid_arguments",
|
|
message: "Expected a surface color.",
|
|
details: nil
|
|
)
|
|
)
|
|
return
|
|
}
|
|
|
|
self.updateColors(
|
|
colorValue: colorValue.uint32Value,
|
|
backdropColorValue: (arguments["backdropColor"] as? NSNumber)?.uint32Value,
|
|
glassTintColorValue: (arguments["glassTintColor"] as? NSNumber)?.uint32Value
|
|
)
|
|
result(nil)
|
|
case "updateGeometry":
|
|
guard
|
|
let arguments = call.arguments as? [String: Any],
|
|
let minimumRadius = arguments["minimumRadius"] as? NSNumber
|
|
else {
|
|
result(
|
|
FlutterError(
|
|
code: "invalid_arguments",
|
|
message: "Expected a minimum corner radius.",
|
|
details: nil
|
|
)
|
|
)
|
|
return
|
|
}
|
|
self.applyInterfaceStyle(from: arguments["brightness"])
|
|
self.applyCornerConfiguration(
|
|
minimumRadius: minimumRadius.doubleValue,
|
|
capsule: arguments["capsule"] as? Bool == true
|
|
)
|
|
result(nil)
|
|
default:
|
|
result(FlutterMethodNotImplemented)
|
|
}
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
channel.setMethodCallHandler(nil)
|
|
}
|
|
|
|
func view() -> UIView {
|
|
rootView
|
|
}
|
|
|
|
private func updateColors(
|
|
colorValue: UInt32,
|
|
backdropColorValue: UInt32?,
|
|
glassTintColorValue: UInt32?
|
|
) {
|
|
let surfaceColor = Self.color(from: colorValue)
|
|
if let glassView = surfaceView as? UIVisualEffectView {
|
|
if #available(iOS 26.0, *), let effect = glassView.effect as? UIGlassEffect {
|
|
effect.tintColor = glassTintColorValue.map { Self.color(from: $0) }
|
|
glassView.effect = effect
|
|
}
|
|
glassView.backgroundColor = .clear
|
|
glassView.contentView.backgroundColor = surfaceColor.withAlphaComponent(min(surfaceColor.cgColor.alpha, 0.12))
|
|
} else {
|
|
surfaceView.backgroundColor = surfaceColor
|
|
}
|
|
rootView.isOpaque = !usesGlass && backdropColorValue != nil
|
|
rootView.backgroundColor = backdropColorValue.map { Self.color(from: $0) } ?? .clear
|
|
}
|
|
|
|
private func applyCornerConfiguration(minimumRadius: CGFloat, capsule: Bool) {
|
|
if #available(iOS 26.0, *) {
|
|
if capsule {
|
|
surfaceView.cornerConfiguration = .capsule()
|
|
return
|
|
}
|
|
let radius = UICornerRadius.containerConcentric(minimum: minimumRadius)
|
|
surfaceView.cornerConfiguration =
|
|
corners == "bottom"
|
|
? .uniformBottomRadius(
|
|
radius,
|
|
topLeftRadius: nil,
|
|
topRightRadius: nil
|
|
)
|
|
: topRadius.map {
|
|
.uniformBottomRadius(
|
|
radius,
|
|
topLeftRadius: .fixed($0),
|
|
topRightRadius: .fixed($0)
|
|
)
|
|
} ?? .uniformCorners(radius: radius)
|
|
} else {
|
|
surfaceView.layer.cornerRadius = minimumRadius
|
|
if corners == "bottom" {
|
|
surfaceView.layer.maskedCorners = [
|
|
.layerMinXMaxYCorner,
|
|
.layerMaxXMaxYCorner,
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
private func applyInterfaceStyle(from value: Any?) {
|
|
switch value as? String {
|
|
case "dark":
|
|
rootView.overrideUserInterfaceStyle = .dark
|
|
case "light":
|
|
rootView.overrideUserInterfaceStyle = .light
|
|
default:
|
|
rootView.overrideUserInterfaceStyle = .unspecified
|
|
}
|
|
}
|
|
|
|
private static func color(from value: UInt32) -> UIColor {
|
|
let alpha = CGFloat((value >> 24) & 0xFF) / 255
|
|
let red = CGFloat((value >> 16) & 0xFF) / 255
|
|
let green = CGFloat((value >> 8) & 0xFF) / 255
|
|
let blue = CGFloat(value & 0xFF) / 255
|
|
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
|
|
}
|
|
}
|