mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Add a click listener to the message to display the emoji selector and message functions
This commit is contained in:
parent
39363f1096
commit
4667301464
12 changed files with 846 additions and 432 deletions
|
|
@ -125,6 +125,7 @@
|
|||
D78E06302BEA6A4A00CE3783 /* ChangeLayoutBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E062F2BEA6A4A00CE3783 /* ChangeLayoutBottomSheet.swift */; };
|
||||
D79622342B1DFE600037EACD /* DialerBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79622332B1DFE600037EACD /* DialerBottomSheet.swift */; };
|
||||
D796F2002B0BB61A0041115F /* ToastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D796F1FF2B0BB61A0041115F /* ToastViewModel.swift */; };
|
||||
D79F2D0A2C47F4BF0038FA07 /* TouchFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79F2D092C47F4BF0038FA07 /* TouchFeedback.swift */; };
|
||||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */; };
|
||||
D7A03FC02ACC2E390081A588 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBF2ACC2E390081A588 /* HistoryView.swift */; };
|
||||
D7A03FC62ACC458A0081A588 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FC52ACC458A0081A588 /* SplashScreen.swift */; };
|
||||
|
|
@ -307,6 +308,7 @@
|
|||
D78E062F2BEA6A4A00CE3783 /* ChangeLayoutBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLayoutBottomSheet.swift; sourceTree = "<group>"; };
|
||||
D79622332B1DFE600037EACD /* DialerBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialerBottomSheet.swift; sourceTree = "<group>"; };
|
||||
D796F1FF2B0BB61A0041115F /* ToastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastViewModel.swift; sourceTree = "<group>"; };
|
||||
D79F2D092C47F4BF0038FA07 /* TouchFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchFeedback.swift; sourceTree = "<group>"; };
|
||||
D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = "<group>"; };
|
||||
D7A03FBF2ACC2E390081A588 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
|
||||
D7A03FC52ACC458A0081A588 /* SplashScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -494,6 +496,7 @@
|
|||
D7173EBD2B7A5C0A00BCC481 /* LinphoneUtils.swift */,
|
||||
C67586AF2C09F247002E77BF /* URIHandler.swift */,
|
||||
C6A5A9462C10B64A0070FEA4 /* SingleSignOn */,
|
||||
D79F2D092C47F4BF0038FA07 /* TouchFeedback.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1114,6 +1117,7 @@
|
|||
C67586AE2C09F23C002E77BF /* URLExtension.swift in Sources */,
|
||||
6613A0B02BAEB7F4008923A4 /* MeetingsListFragment.swift in Sources */,
|
||||
D76005F62B0798B00054B79A /* IntExtension.swift in Sources */,
|
||||
D79F2D0A2C47F4BF0038FA07 /* TouchFeedback.swift in Sources */,
|
||||
D78E06282BE3811D00CE3783 /* CallStatsModel.swift in Sources */,
|
||||
D7E6D0512AEBDBD500A57AAF /* ContactsListBottomSheet.swift in Sources */,
|
||||
D75759322B56D40900E7AC10 /* ZRTPPopup.swift in Sources */,
|
||||
|
|
|
|||
21
Linphone/Assets.xcassets/forward.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/forward.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "forward.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
5
Linphone/Assets.xcassets/forward.imageset/forward.svg
vendored
Normal file
5
Linphone/Assets.xcassets/forward.imageset/forward.svg
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Répondre">
|
||||
<path id="Vector" d="M25.4222 14.19L17.0891 22.5231C16.9726 22.6398 16.824 22.7193 16.6623 22.7515C16.5006 22.7837 16.3329 22.7672 16.1806 22.7041C16.0282 22.6409 15.898 22.534 15.8065 22.3969C15.7149 22.2597 15.6661 22.0985 15.6662 21.9336V17.791C9.71848 18.1285 5.64046 21.9856 4.49569 23.2075C4.31594 23.3995 4.08022 23.5298 3.82207 23.5801C3.56392 23.6303 3.2965 23.5979 3.05787 23.4873C2.81924 23.3768 2.62156 23.1938 2.49296 22.9644C2.36435 22.735 2.31138 22.4708 2.34158 22.2096C2.72803 18.8493 4.56861 15.6171 7.52478 13.1088C9.97993 11.0255 12.9642 9.70366 15.6662 9.47242V5.26732C15.6661 5.10241 15.7149 4.94117 15.8065 4.80401C15.898 4.66685 16.0282 4.55994 16.1806 4.49682C16.3329 4.43369 16.5006 4.41719 16.6623 4.4494C16.824 4.48161 16.9726 4.56108 17.0891 4.67775L25.4222 13.0109C25.4997 13.0883 25.5612 13.1802 25.6031 13.2813C25.6451 13.3825 25.6666 13.4909 25.6666 13.6004C25.6666 13.71 25.6451 13.8184 25.6031 13.9195C25.5612 14.0207 25.4997 14.1126 25.4222 14.19Z" fill="#6C7A87"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm48-88a8,8,0,0,1-8,8H136v32a8,8,0,0,1-16,0V136H88a8,8,0,0,1,0-16h32V88a8,8,0,0,1,16,0v32h32A8,8,0,0,1,176,128Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm48-88a8,8,0,0,1-8,8H136v32a8,8,0,0,1-16,0V136H88a8,8,0,0,1,0-16h32V88a8,8,0,0,1,16,0v32h32A8,8,0,0,1,176,128Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 340 B |
21
Linphone/Assets.xcassets/reply.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/reply.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reply.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
5
Linphone/Assets.xcassets/reply.imageset/reply.svg
vendored
Normal file
5
Linphone/Assets.xcassets/reply.imageset/reply.svg
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Répondre">
|
||||
<path id="Vector" d="M2.57775 14.19L10.9109 22.5231C11.0274 22.6398 11.176 22.7193 11.3377 22.7515C11.4994 22.7837 11.6671 22.7672 11.8194 22.7041C11.9718 22.6409 12.102 22.534 12.1935 22.3969C12.2851 22.2597 12.3339 22.0985 12.3338 21.9336V17.791C18.2815 18.1285 22.3595 21.9856 23.5043 23.2075C23.6841 23.3995 23.9198 23.5298 24.1779 23.5801C24.4361 23.6303 24.7035 23.5979 24.9421 23.4873C25.1808 23.3768 25.3784 23.1938 25.507 22.9644C25.6356 22.735 25.6886 22.4708 25.6584 22.2096C25.272 18.8493 23.4314 15.6171 20.4752 13.1088C18.0201 11.0255 15.0358 9.70366 12.3338 9.47242V5.26732C12.3339 5.10241 12.2851 4.94117 12.1935 4.80401C12.102 4.66685 11.9718 4.55994 11.8194 4.49682C11.6671 4.43369 11.4994 4.41719 11.3377 4.4494C11.176 4.48161 11.0274 4.56108 10.9109 4.67775L2.57775 13.0109C2.50027 13.0883 2.43881 13.1802 2.39688 13.2813C2.35494 13.3825 2.33335 13.4909 2.33335 13.6004C2.33335 13.71 2.35494 13.8184 2.39688 13.9195C2.43881 14.0207 2.50027 14.1126 2.57775 14.19Z" fill="#6C7A87"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -132,6 +132,21 @@
|
|||
},
|
||||
"|" : {
|
||||
|
||||
},
|
||||
"❤️" : {
|
||||
|
||||
},
|
||||
"👍" : {
|
||||
|
||||
},
|
||||
"😂" : {
|
||||
|
||||
},
|
||||
"😢" : {
|
||||
|
||||
},
|
||||
"😮" : {
|
||||
|
||||
},
|
||||
"0" : {
|
||||
|
||||
|
|
@ -1468,6 +1483,91 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"menu_copy_chat_message" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Copy"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Copier le texte"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu_delete_selected_item" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Delete"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Supprimer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu_forward_chat_message" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Forward"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Transférer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu_reply_to_chat_message" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Reply"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Répondre"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu_resend_chat_message" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Re-send"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Ré-envoyer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Message" : {
|
||||
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,10 +28,14 @@ struct ChatBubbleView: View {
|
|||
|
||||
let geometryProxy: GeometryProxy
|
||||
|
||||
@State private var ticker = Ticker()
|
||||
@State private var isPressed: Bool = false
|
||||
@State private var timePassed: TimeInterval?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if !message.text.isEmpty || !message.attachments.isEmpty {
|
||||
HStack {
|
||||
HStack(alignment: .top, content: {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
|
@ -44,19 +48,11 @@ struct ChatBubbleView: View {
|
|||
avatarSize: 35
|
||||
)
|
||||
.padding(.top, 30)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing {
|
||||
VStack {
|
||||
Avatar(
|
||||
contactAvatarModel: ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
|
||||
avatarSize: 35
|
||||
)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.hidden()
|
||||
.padding(.leading, 43)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
|
|
@ -67,33 +63,6 @@ struct ChatBubbleView: View {
|
|||
.padding(.bottom, 2)
|
||||
}
|
||||
ZStack {
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && message.isFirstMessage {
|
||||
VStack {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack {
|
||||
}
|
||||
.frame(width: 15, height: 15)
|
||||
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 2))
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
|
|
@ -137,7 +106,11 @@ struct ChatBubbleView: View {
|
|||
}
|
||||
.padding(.all, 15)
|
||||
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 3))
|
||||
.roundedCorner(
|
||||
16,
|
||||
corners: message.isOutgoing && message.isFirstMessage ? [.topLeft, .topRight, .bottomLeft] :
|
||||
(!message.isOutgoing && message.isFirstMessage ? [.topRight, .bottomRight, .bottomLeft] : [.allCorners]))
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
|
|
@ -150,11 +123,33 @@ struct ChatBubbleView: View {
|
|||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.leading, message.isOutgoing ? 40 : 0)
|
||||
.padding(.trailing, !message.isOutgoing ? 40 : 0)
|
||||
}
|
||||
}
|
||||
.onTapGesture {}
|
||||
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { (value) in
|
||||
self.isPressed = value
|
||||
if value == true {
|
||||
self.timePassed = 0
|
||||
self.ticker.start(interval: 0.2)
|
||||
}
|
||||
|
||||
}, perform: {})
|
||||
.onReceive(ticker.objectWillChange) { (_) in
|
||||
// Stop timer and reset the start date if the button in not pressed
|
||||
guard self.isPressed else {
|
||||
self.ticker.stop()
|
||||
return
|
||||
}
|
||||
|
||||
self.timePassed = self.ticker.timeIntervalSinceStarted
|
||||
withAnimation {
|
||||
conversationViewModel.selectedMessage = message
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
@ -375,6 +370,49 @@ struct GifImageView: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
|
||||
class Ticker: ObservableObject {
|
||||
|
||||
var startedAt: Date = Date()
|
||||
|
||||
var timeIntervalSinceStarted: TimeInterval {
|
||||
return Date().timeIntervalSince(startedAt)
|
||||
}
|
||||
|
||||
private var timer: Timer?
|
||||
func start(interval: TimeInterval) {
|
||||
stop()
|
||||
startedAt = Date()
|
||||
timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
timer?.invalidate()
|
||||
}
|
||||
|
||||
deinit {
|
||||
timer?.invalidate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RoundedCorner: Shape {
|
||||
var radius: CGFloat = .infinity
|
||||
var corners: UIRectCorner = .allCorners
|
||||
|
||||
func path(in rect: CGRect) -> Path {
|
||||
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
||||
return Path(path.cgPath)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func roundedCorner(_ radius: CGFloat, corners: UIRectCorner) -> some View {
|
||||
clipShape(RoundedCorner(radius: radius, corners: corners) )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#Preview {
|
||||
ChatBubbleView(conversationViewModel: ConversationViewModel(), index: 0)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -138,11 +138,13 @@ struct ConversationsListFragment: View {
|
|||
if index < conversationsListViewModel.conversationsList.count {
|
||||
if conversationViewModel.displayedConversation != nil {
|
||||
conversationViewModel.displayedConversation = nil
|
||||
conversationViewModel.selectedMessage = nil
|
||||
conversationViewModel.resetMessage()
|
||||
conversationViewModel.changeDisplayedChatRoom(conversationModel: conversationsListViewModel.conversationsList[index])
|
||||
|
||||
conversationViewModel.getMessages()
|
||||
} else {
|
||||
conversationViewModel.selectedMessage = nil
|
||||
withAnimation {
|
||||
conversationViewModel.changeDisplayedChatRoom(conversationModel: conversationsListViewModel.conversationsList[index])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ class ConversationViewModel: ObservableObject {
|
|||
@Published var mediasToSend: [Attachment] = []
|
||||
var maxMediaCount = 12
|
||||
|
||||
@Published var selectedMessage: Message?
|
||||
|
||||
init() {}
|
||||
|
||||
func addConversationDelegate() {
|
||||
|
|
|
|||
30
Linphone/Utils/TouchFeedback.swift
Normal file
30
Linphone/Utils/TouchFeedback.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import CoreHaptics
|
||||
import AudioToolbox
|
||||
|
||||
func touchFeedback() {
|
||||
if CHHapticEngine.capabilitiesForHardware().supportsHaptics {
|
||||
UIImpactFeedbackGenerator().impactOccurred()
|
||||
} else {
|
||||
AudioServicesPlaySystemSound(1519) // 1520 and 1521 are gradually stronger
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue