mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
App polish part 3 (#1135)
- Add an animation to long press on messages. - Make emojis bigger on the reactions picker screen. - Remove quote menu action. - Fix emoji picker handle overlapping focused search field. - Update launch, splash and onboarding screens. - Use elevation tokens in Forms. - Move the cancel button on the Start Chat flow.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// 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
|
||||
|
||||
struct LongPressWithFeedback: ViewModifier {
|
||||
let action: () -> Void
|
||||
|
||||
@State private var isLongPressing = false
|
||||
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.scaleEffect(x: isLongPressing ? 0.93 : 1,
|
||||
y: isLongPressing ? 0.93 : 1)
|
||||
.animation(isLongPressing ? .spring(response: 0.8).delay(0.2) : .spring(response: 0.2),
|
||||
value: isLongPressing)
|
||||
.onLongPressGesture(minimumDuration: 0.25) {
|
||||
action()
|
||||
feedbackGenerator.impactOccurred()
|
||||
} onPressingChanged: { isPressing in
|
||||
isLongPressing = isPressing
|
||||
if isPressing {
|
||||
feedbackGenerator.prepare()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func longPressWithFeedback(action: @escaping () -> Void) -> some View {
|
||||
modifier(LongPressWithFeedback(action: action))
|
||||
}
|
||||
}
|
||||
|
||||
struct FakeContextMenu_Previews: PreviewProvider {
|
||||
static var previews: some View { Preview() }
|
||||
|
||||
struct Preview: View {
|
||||
private let viewModel = RoomScreenViewModel.mock
|
||||
@State private var isPresentingSheet = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
mockBubble("This is a message from somebody with a couple of lines of text.")
|
||||
.longPressWithFeedback { isPresentingSheet = true }
|
||||
|
||||
mockBubble("Short message")
|
||||
.longPressWithFeedback { isPresentingSheet = true }
|
||||
|
||||
mockBubble("How are you today? The sun is shining here and its very hot ☀️☀️☀️")
|
||||
.longPressWithFeedback { isPresentingSheet = true }
|
||||
|
||||
mockBubble("I'm a fake!")
|
||||
.contextMenu {
|
||||
Button("Copy") { }
|
||||
Button("Reply") { }
|
||||
Button("Remove") { }
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle("Work chat")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
.sheet(isPresented: $isPresentingSheet) {
|
||||
Text("Long pressed!")
|
||||
.presentationDetents([.medium])
|
||||
}
|
||||
.environmentObject(viewModel.context)
|
||||
}
|
||||
|
||||
func mockBubble(_ body: String) -> some View {
|
||||
Text(body)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.compound._bgBubbleOutgoing, in: RoundedRectangle(cornerRadius: 12))
|
||||
.onTapGesture { /* Fix long press gesture blocking the scroll view */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,6 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
@Environment(\.timelineGroupStyle) private var timelineGroupStyle
|
||||
|
||||
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .heavy)
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@@ -116,13 +114,8 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
}
|
||||
// We need a tap gesture before this long one so that it doesn't
|
||||
// steal away the gestures from the scroll view
|
||||
.onLongPressGesture(minimumDuration: 0.25) {
|
||||
.longPressWithFeedback {
|
||||
context.send(viewAction: .timelineItemMenu(itemID: timelineItem.id))
|
||||
feedbackGenerator.impactOccurred()
|
||||
} onPressingChanged: { pressing in
|
||||
if pressing {
|
||||
feedbackGenerator.prepare()
|
||||
}
|
||||
}
|
||||
.padding(.top, messageBubbleTopPadding)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ struct TimelineItemMenuActions {
|
||||
enum TimelineItemMenuAction: Identifiable, Hashable {
|
||||
case copy
|
||||
case edit
|
||||
case quote
|
||||
case copyPermalink
|
||||
case redact
|
||||
case reply
|
||||
@@ -182,10 +181,6 @@ public struct TimelineItemMenu: View {
|
||||
Button { send(action) } label: {
|
||||
MenuLabel(title: L10n.actionEdit, systemImageName: "pencil.line")
|
||||
}
|
||||
case .quote:
|
||||
Button { send(action) } label: {
|
||||
MenuLabel(title: L10n.actionQuote, systemImageName: "quote.bubble")
|
||||
}
|
||||
case .copyPermalink:
|
||||
Button { send(action) } label: {
|
||||
MenuLabel(title: L10n.actionCopyLinkToMessage, systemImageName: "link")
|
||||
|
||||
Reference in New Issue
Block a user