Skip to content

Commit

Permalink
Merge branch 'master' into issue-618-cont-voice-recording
Browse files Browse the repository at this point in the history
Signed-off-by: Julius Linus <69230048+rapterjet2004@users.noreply.github.com>
  • Loading branch information
rapterjet2004 authored Oct 10, 2024
2 parents 60a7418 + 9d697e4 commit 4be805e
Show file tree
Hide file tree
Showing 117 changed files with 2,065 additions and 1,261 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-feedback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ jobs:
(If you believe you should not receive this message, you can add yourself to the [blocklist](https://github.com/nextcloud/.github/blob/master/non-community-usernames.txt).)
days-before-feedback: 14
start-date: '2024-04-30'
exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }},nextcloud-command,nextcloud-android-bot'
exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }}'
exempt-bots: true
9 changes: 5 additions & 4 deletions .github/workflows/uitests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
paths:
- '.github/workflows/**'
- Podfile
- NextcloudTalk.xcodeproj/**
- NextcloudTalk/**
- NextcloudTalkTests/**
Expand Down Expand Up @@ -83,10 +84,10 @@ jobs:
configs: [
{ talkbranch: 'stable23', serverbranch: 'stable23', phpversion: '8.0' },
{ talkbranch: 'stable27', serverbranch: 'stable27', phpversion: '8.2' },
{ talkbranch: 'stable28', serverbranch: 'stable28', phpversion: '8.2' },
{ talkbranch: 'stable29', serverbranch: 'stable29', phpversion: '8.2' },
{ talkbranch: 'stable30', serverbranch: 'stable30', phpversion: '8.2' },
{ talkbranch: 'main', serverbranch: 'master', phpversion: '8.2' }
{ talkbranch: 'stable28', serverbranch: 'stable28', phpversion: '8.3' },
{ talkbranch: 'stable29', serverbranch: 'stable29', phpversion: '8.3' },
{ talkbranch: 'stable30', serverbranch: 'stable30', phpversion: '8.3' },
{ talkbranch: 'main', serverbranch: 'master', phpversion: '8.3' }
]

steps:
Expand Down
1 change: 1 addition & 0 deletions .pyspelling.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ nextcloud
CallKit
Unban
unban
Zammad
148 changes: 119 additions & 29 deletions NextcloudTalk.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

115 changes: 115 additions & 0 deletions NextcloudTalk/AudioPlayerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-3.0-or-later
//

import UIKit

protocol AudioPlayerViewDelegate: AnyObject {

func audioPlayerPlayButtonPressed()
func audioPlayerPauseButtonPressed()
func audioPlayerProgressChanged(progress: CGFloat)
}

class AudioPlayerView: UIView {

@IBOutlet var contentView: UIView!
@IBOutlet weak var playPauseButton: UIButton!
@IBOutlet weak var slider: UISlider!
@IBOutlet weak var durationLabel: UILabel!

var isPlaying: Bool = false

public weak var delegate: AudioPlayerViewDelegate?

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

func commonInit() {
Bundle.main.loadNibNamed("AudioPlayerView", owner: self, options: nil)

contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

// Play/Pause button
playPauseButton.addAction(for: .touchUpInside) { [unowned self] in
if isPlaying {
delegate?.audioPlayerPauseButtonPressed()
} else {
delegate?.audioPlayerPlayButtonPressed()
}
}

// Slider
let thumbImage = UIImage(systemName: "circle.fill")?
.withConfiguration(UIImage.SymbolConfiguration(pointSize: 16))
.withTintColor(.label, renderingMode: .alwaysOriginal)
slider.setThumbImage(thumbImage, for: .normal)
slider.semanticContentAttribute = .forceLeftToRight
slider.addAction(for: .valueChanged) { [unowned self] in
delegate?.audioPlayerProgressChanged(progress: CGFloat(slider.value))
}

// Duration label
hideDurationLabel()

backgroundColor = .secondarySystemBackground
layer.cornerRadius = 8.0
layer.masksToBounds = true

self.addSubview(contentView)
}

func setPlayerProgress(_ progress: CGFloat, isPlaying playing: Bool, maximumValue maxValue: CGFloat) {
setPlayPauseButton(playing: playing)
slider.isEnabled = true
slider.value = Float(progress)
slider.maximumValue = Float(maxValue)
setDurationLabel(progress: progress, duration: maxValue)
slider.setNeedsLayout()
}

func resetPlayer() {
setPlayPauseButton(playing: false)
slider.isEnabled = false
slider.value = 0
hideDurationLabel()
slider.setNeedsLayout()
}

func setPlayPauseButton(playing: Bool) {
isPlaying = playing

if isPlaying {
playPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
} else {
playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
}
}

func setDurationLabel(progress: CGFloat, duration: CGFloat) {
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.minute, .second]
dateComponentsFormatter.zeroFormattingBehavior = []

let progressTime = dateComponentsFormatter.string(from: TimeInterval(progress)) ?? "0:00"
let durationTime = dateComponentsFormatter.string(from: TimeInterval(duration)) ?? "0:00"

let playerTimeString = "\(progressTime)".withTextColor(.label).withFont(.systemFont(ofSize: 13, weight: .medium))
playerTimeString.append(" / \(durationTime)".withTextColor(.secondaryLabel).withFont(.systemFont(ofSize: 13)))

durationLabel.attributedText = playerTimeString
}

func hideDurationLabel() {
durationLabel.text = ""
}
}
67 changes: 67 additions & 0 deletions NextcloudTalk/AudioPlayerView.xib
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AudioPlayerView" customModule="NextcloudTalk" customModuleProvider="target">
<connections>
<outlet property="contentView" destination="iN0-l3-epB" id="WHP-2s-6Il"/>
<outlet property="durationLabel" destination="7vy-DL-v2I" id="gLr-z8-lYD"/>
<outlet property="playPauseButton" destination="weF-VJ-kQg" id="6O8-WK-cv9"/>
<outlet property="slider" destination="Cgz-Rx-FyJ" id="TME-tZ-DL1"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="441" height="52"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="weF-VJ-kQg">
<rect key="frame" x="8" y="4" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="TwB-9N-gEK"/>
<constraint firstAttribute="height" constant="44" id="XIa-Qd-GZD"/>
</constraints>
<color key="tintColor" systemColor="labelColor"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" image="play.fill" catalog="system"/>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="0:00 / 0:02" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7vy-DL-v2I">
<rect key="frame" x="366" y="0.0" width="67" height="52"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="Cgz-Rx-FyJ">
<rect key="frame" x="58" y="11" width="302" height="31"/>
</slider>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<constraints>
<constraint firstItem="Cgz-Rx-FyJ" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="0pD-pS-YhM"/>
<constraint firstAttribute="bottom" secondItem="7vy-DL-v2I" secondAttribute="bottom" id="1PB-ja-hxc"/>
<constraint firstItem="Cgz-Rx-FyJ" firstAttribute="leading" secondItem="weF-VJ-kQg" secondAttribute="trailing" constant="8" symbolic="YES" id="H3b-Y3-6vI"/>
<constraint firstItem="weF-VJ-kQg" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="MAo-xn-Ott"/>
<constraint firstAttribute="bottom" secondItem="weF-VJ-kQg" secondAttribute="bottom" constant="4" id="Ms8-VQ-WhR"/>
<constraint firstItem="weF-VJ-kQg" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="8" id="QBV-dj-HsA"/>
<constraint firstItem="7vy-DL-v2I" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="QFI-XE-9Fd"/>
<constraint firstAttribute="trailing" secondItem="7vy-DL-v2I" secondAttribute="trailing" constant="8" id="VnI-wM-6MZ"/>
<constraint firstItem="7vy-DL-v2I" firstAttribute="leading" secondItem="Cgz-Rx-FyJ" secondAttribute="trailing" constant="8" id="nHt-Db-jET"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="130.53435114503816" y="-233.09859154929578"/>
</view>
</objects>
<resources>
<image name="play.fill" catalog="system" width="120" height="128"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
4 changes: 3 additions & 1 deletion NextcloudTalk/BannedActorTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
self.tableView.register(UINib(nibName: bannedActorCellIdentifier, bundle: nil), forCellReuseIdentifier: bannedActorCellIdentifier)
self.tableView.backgroundView = backgroundView

// We only want the loadingView, so always hide the placeholder
self.backgroundView.placeholderView.isHidden = true
self.backgroundView.placeholderTextView.text = NSLocalizedString("No banned users or guests", comment: "")
self.backgroundView.setImage(UIImage(systemName: "person.badge.minus"))
self.backgroundView.loadingView.startAnimating()

self.modifyingViewIndicator.color = NCAppBranding.themeTextColor()
Expand All @@ -62,6 +63,7 @@

self.backgroundView.loadingView.stopAnimating()
self.backgroundView.loadingView.isHidden = true
self.backgroundView.placeholderView.isHidden = !self.bannedActors.isEmpty

self.tableView.reloadData()
self.hideActivityIndicator()
Expand Down
55 changes: 55 additions & 0 deletions NextcloudTalk/BaseChatTableViewCell+Audio.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-3.0-or-later
//

extension BaseChatTableViewCell {

func setupForAudioCell(with message: NCChatMessage) {
if self.audioPlayerView == nil {
// Audio player view
let audioPlayerView = AudioPlayerView(frame: CGRect(x: 0, y: 0, width: 0, height: voiceMessageCellPlayerHeight))
self.audioPlayerView = audioPlayerView
self.audioPlayerView?.delegate = self

audioPlayerView.translatesAutoresizingMaskIntoConstraints = false

self.messageBodyView.addSubview(audioPlayerView)

NSLayoutConstraint.activate([
audioPlayerView.leftAnchor.constraint(equalTo: self.messageBodyView.leftAnchor),
audioPlayerView.rightAnchor.constraint(equalTo: self.messageBodyView.rightAnchor),
audioPlayerView.topAnchor.constraint(equalTo: self.messageBodyView.topAnchor)
])
}
}

func prepareForReuseAudioCell() {
self.audioPlayerView?.resetPlayer()
self.clearFileStatusView()
}

func audioPlayerPlayButtonPressed() {
guard let audioFileParameter = message?.file() else {
return
}

self.delegate?.cellWants(toPlayAudioFile: audioFileParameter)
}

func audioPlayerPauseButtonPressed() {
guard let audioFileParameter = message?.file() else {
return
}

self.delegate?.cellWants(toPauseAudioFile: audioFileParameter)
}

func audioPlayerProgressChanged(progress: CGFloat) {
guard let audioFileParameter = message?.file() else {
return
}

self.delegate?.cellWants(toChangeProgress: progress, fromAudioFile: audioFileParameter)
}
}
Loading

0 comments on commit 4be805e

Please sign in to comment.