Compare commits

...

4 Commits

15 changed files with 290 additions and 70 deletions

View File

@ -55,6 +55,7 @@ extension Notification {
case favourite case favourite
case follow case follow
case followRequest = "follow_request" case followRequest = "follow_request"
case poll
case unknown case unknown
} }
} }

View File

@ -158,6 +158,8 @@
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */; }; D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */; };
D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */; }; D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */; };
D6620ACE2511A0ED00312CA0 /* StatusStateResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */; }; D6620ACE2511A0ED00312CA0 /* StatusStateResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */; };
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D662AEED263A3B880082A153 /* PollFinishedTableViewCell.swift */; };
D662AEF0263A3B880082A153 /* PollFinishedTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D662AEEE263A3B880082A153 /* PollFinishedTableViewCell.xib */; };
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; }; D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; };
D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */; }; D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */; };
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626121360B1900C9CBA2 /* Preferences.swift */; }; D663626221360B1900C9CBA2 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626121360B1900C9CBA2 /* Preferences.swift */; };
@ -217,7 +219,6 @@
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */; }; D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */; };
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */; }; D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */; };
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9F240C8384002843CE /* EmojiLabel.swift */; }; D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9F240C8384002843CE /* EmojiLabel.swift */; };
D6969EA4240DD28D002843CE /* UnknownNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */; };
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */ = {isa = PBXBuildFile; productRef = D69CCBBE249E6EFD000AF167 /* CrashReporter */; }; D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */ = {isa = PBXBuildFile; productRef = D69CCBBE249E6EFD000AF167 /* CrashReporter */; };
D6A00B1D26379FC900316AD4 /* PollOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */; }; D6A00B1D26379FC900316AD4 /* PollOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */; };
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; }; D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; };
@ -535,6 +536,8 @@
D65F613523AFD65900F3CFD3 /* Ambassador.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Ambassador.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D65F613523AFD65900F3CFD3 /* Ambassador.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Ambassador.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D65F613723AFD65D00F3CFD3 /* Embassy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Embassy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D65F613723AFD65D00F3CFD3 /* Embassy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Embassy.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusStateResolver.swift; sourceTree = "<group>"; }; D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusStateResolver.swift; sourceTree = "<group>"; };
D662AEED263A3B880082A153 /* PollFinishedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFinishedTableViewCell.swift; sourceTree = "<group>"; };
D662AEEE263A3B880082A153 /* PollFinishedTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PollFinishedTableViewCell.xib; sourceTree = "<group>"; };
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConversationMainStatusTableViewCell.xib; sourceTree = "<group>"; }; D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConversationMainStatusTableViewCell.xib; sourceTree = "<group>"; };
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMainStatusTableViewCell.swift; sourceTree = "<group>"; }; D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMainStatusTableViewCell.swift; sourceTree = "<group>"; };
D663626121360B1900C9CBA2 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; }; D663626121360B1900C9CBA2 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
@ -594,7 +597,6 @@
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeSceneDelegate.swift; sourceTree = "<group>"; }; D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeSceneDelegate.swift; sourceTree = "<group>"; };
D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+Emoji.swift"; sourceTree = "<group>"; }; D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+Emoji.swift"; sourceTree = "<group>"; };
D6969E9F240C8384002843CE /* EmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLabel.swift; sourceTree = "<group>"; }; D6969E9F240C8384002843CE /* EmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLabel.swift; sourceTree = "<group>"; };
D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UnknownNotificationTableViewCell.xib; sourceTree = "<group>"; };
D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionsView.swift; sourceTree = "<group>"; }; D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionsView.swift; sourceTree = "<group>"; };
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; }; D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; };
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; }; D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; };
@ -1185,7 +1187,8 @@
D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */, D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */,
D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */, D64BC18D23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift */,
D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */, D64BC18E23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib */,
D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */, D662AEED263A3B880082A153 /* PollFinishedTableViewCell.swift */,
D662AEEE263A3B880082A153 /* PollFinishedTableViewCell.xib */,
); );
path = Notifications; path = Notifications;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1792,7 +1795,6 @@
D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */, D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */,
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */, D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */, D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */,
D6969EA4240DD28D002843CE /* UnknownNotificationTableViewCell.xib in Resources */,
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */, D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */, D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */,
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */, D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
@ -1803,6 +1805,7 @@
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */, D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */,
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */, D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */,
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */, D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */,
D662AEF0263A3B880082A153 /* PollFinishedTableViewCell.xib in Resources */,
D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */, D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -1920,6 +1923,7 @@
D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */, D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */,
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */, D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */,
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */, 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */,
D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */, D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */,
D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */, D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */,
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */, D60E2F292442372B005F8713 /* AccountMO.swift in Sources */,

View File

@ -32,7 +32,11 @@ struct ComposeContentWarningTextField: UIViewRepresentable {
} }
func updateUIView(_ uiView: UITextField, context: Context) { func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text if context.coordinator.skipSettingTextOnNextUpdate {
context.coordinator.skipSettingTextOnNextUpdate = false
} else {
uiView.text = text
}
} }
func makeCoordinator() -> Coordinator { func makeCoordinator() -> Coordinator {
@ -44,6 +48,8 @@ struct ComposeContentWarningTextField: UIViewRepresentable {
var text: Binding<String>! var text: Binding<String>!
var uiState: ComposeUIState! var uiState: ComposeUIState!
var skipSettingTextOnNextUpdate = false
@objc func didChange(_ textField: UITextField) { @objc func didChange(_ textField: UITextField) {
text.wrappedValue = textField.text ?? "" text.wrappedValue = textField.text ?? ""
} }
@ -57,6 +63,12 @@ struct ComposeContentWarningTextField: UIViewRepresentable {
updateAutocompleteState(textField: textField) updateAutocompleteState(textField: textField)
} }
func textFieldDidChangeSelection(_ textField: UITextField) {
// see MainComposeTextView.Coordinator.textViewDidChangeSelection(_:)
skipSettingTextOnNextUpdate = true
self.updateAutocompleteState(textField: textField)
}
func autocomplete(with string: String) { func autocomplete(with string: String) {
guard let textField = textField, guard let textField = textField,
let text = textField.text, let text = textField.text,

View File

@ -135,7 +135,11 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
} }
func updateUIView(_ uiView: UITextView, context: Context) { func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text if context.coordinator.skipSettingTextOnNextUpdate {
context.coordinator.skipSettingTextOnNextUpdate = false
} else {
uiView.text = text
}
if let visibilityButton = visibilityButton { if let visibilityButton = visibilityButton {
visibilityButton.image = UIImage(systemName: visibility.imageName) visibilityButton.image = UIImage(systemName: visibility.imageName)
@ -199,6 +203,8 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
var uiState: ComposeUIState var uiState: ComposeUIState
var caretScrollPositionAnimator: UIViewPropertyAnimator? var caretScrollPositionAnimator: UIViewPropertyAnimator?
var skipSettingTextOnNextUpdate = false
init(text: Binding<String>, uiState: ComposeUIState, didChange: @escaping (UITextView) -> Void) { init(text: Binding<String>, uiState: ComposeUIState, didChange: @escaping (UITextView) -> Void) {
self.text = text self.text = text
self.didChange = didChange self.didChange = didChange
@ -258,6 +264,14 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
updateAutocompleteState() updateAutocompleteState()
} }
func textViewDidChangeSelection(_ textView: UITextView) {
// Setting the text view's text causes it to move the cursor to the end (though only
// when the text contains an emoji :/), so skip setting the text on the next SwiftUI update
// that's triggered by setting the autocomplete state.
skipSettingTextOnNextUpdate = true
self.updateAutocompleteState()
}
func autocomplete(with string: String) { func autocomplete(with string: String) {
guard let textView = textView, guard let textView = textView,
let text = textView.text, let text = textView.text,

View File

@ -15,6 +15,7 @@ class NotificationsTableViewController: TimelineLikeTableViewController<Notifica
private let actionGroupCell = "actionGroupCell" private let actionGroupCell = "actionGroupCell"
private let followGroupCell = "followGroupCell" private let followGroupCell = "followGroupCell"
private let followRequestCell = "followRequestCell" private let followRequestCell = "followRequestCell"
private let pollCell = "pollCell"
private let unknownCell = "unknownCell" private let unknownCell = "unknownCell"
weak var mastodonController: MastodonController! weak var mastodonController: MastodonController!
@ -49,6 +50,7 @@ class NotificationsTableViewController: TimelineLikeTableViewController<Notifica
tableView.register(UINib(nibName: "ActionNotificationGroupTableViewCell", bundle: .main), forCellReuseIdentifier: actionGroupCell) tableView.register(UINib(nibName: "ActionNotificationGroupTableViewCell", bundle: .main), forCellReuseIdentifier: actionGroupCell)
tableView.register(UINib(nibName: "FollowNotificationGroupTableViewCell", bundle: .main), forCellReuseIdentifier: followGroupCell) tableView.register(UINib(nibName: "FollowNotificationGroupTableViewCell", bundle: .main), forCellReuseIdentifier: followGroupCell)
tableView.register(UINib(nibName: "FollowRequestNotificationTableViewCell", bundle: .main), forCellReuseIdentifier: followRequestCell) tableView.register(UINib(nibName: "FollowRequestNotificationTableViewCell", bundle: .main), forCellReuseIdentifier: followRequestCell)
tableView.register(UINib(nibName: "PollFinishedTableViewCell", bundle: .main), forCellReuseIdentifier: pollCell)
tableView.register(UINib(nibName: "BasicTableViewCell", bundle: .main), forCellReuseIdentifier: unknownCell) tableView.register(UINib(nibName: "BasicTableViewCell", bundle: .main), forCellReuseIdentifier: unknownCell)
} }
@ -160,6 +162,13 @@ class NotificationsTableViewController: TimelineLikeTableViewController<Notifica
cell.updateUI(notification: notification) cell.updateUI(notification: notification)
return cell return cell
case .poll:
guard let notification = group.notifications.first,
let cell = tableView.dequeueReusableCell(withIdentifier: pollCell, for: indexPath) as? PollFinishedTableViewCell else { fatalError() }
cell.delegate = self
cell.updateUI(notification: notification)
return cell
case .unknown: case .unknown:
let cell = tableView.dequeueReusableCell(withIdentifier: unknownCell, for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: unknownCell, for: indexPath)
cell.textLabel!.text = NSLocalizedString("Unknown Notification", comment: "unknown notification fallback cell text") cell.textLabel!.text = NSLocalizedString("Unknown Notification", comment: "unknown notification fallback cell text")

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17504.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17124"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>

View File

@ -0,0 +1,108 @@
//
// PollFinishedTableViewCell.swift
// Tusker
//
// Created by Shadowfacts on 4/28/21.
// Copyright © 2021 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
import SwiftSoup
class PollFinishedTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var displayNameLabel: EmojiLabel!
@IBOutlet weak var timestampLabel: UILabel!
@IBOutlet weak var statusContentLabel: UILabel!
@IBOutlet weak var pollView: StatusPollView!
var notification: Pachyderm.Notification?
private var updateTimestampWorkItem: DispatchWorkItem?
func updateUI(notification: Pachyderm.Notification) {
guard let statusID = notification.status?.id,
let status = delegate?.apiController.persistentContainer.status(for: statusID),
let poll = status.poll else {
return
}
self.notification = notification
updateTimestamp()
displayNameLabel.text = notification.account.displayName
displayNameLabel.setEmojis(notification.account.emojis, identifier: notification.account.id)
let doc = try! SwiftSoup.parse(status.content)
statusContentLabel.text = try! doc.text()
pollView.updateUI(status: status, poll: poll)
}
private func updateTimestamp() {
guard let notification = notification else { return }
timestampLabel.text = notification.createdAt.timeAgoString()
let delay: DispatchTimeInterval?
switch notification.createdAt.timeAgo().1 {
case .second:
delay = .seconds(10)
case .minute:
delay = .seconds(60)
default:
delay = nil
}
if let delay = delay {
if updateTimestampWorkItem == nil {
updateTimestampWorkItem = DispatchWorkItem { [weak self] in
self?.updateTimestamp()
}
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
}
} else {
updateTimestampWorkItem = nil
}
}
override func prepareForReuse() {
super.prepareForReuse()
updateTimestampWorkItem?.cancel()
updateTimestampWorkItem = nil
}
}
extension PollFinishedTableViewCell: SelectableTableViewCell {
func didSelectCell() {
guard let delegate = delegate,
let status = notification?.status else {
return
}
let vc = delegate.conversation(mainStatusID: status.id, state: .unknown)
delegate.show(vc)
}
}
extension PollFinishedTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let delegate = delegate,
let statusID = notification?.status?.id,
let status = delegate.apiController.persistentContainer.status(for: statusID) else {
return nil
}
return (content: {
delegate.conversation(mainStatusID: statusID, state: .unknown)
}, actions: {
self.actionsForStatus(status, sourceView: self)
})
}
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<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"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="102" id="KGk-i7-Jjw" customClass="PollFinishedTableViewCell" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="102"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="102"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="eSw-Oo-Scy">
<rect key="frame" x="72" y="11" width="232" height="80"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="69j-GL-yd7">
<rect key="frame" x="0.0" y="0.0" width="232" height="20.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A poll has finished" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9He-JX-i6Z">
<rect key="frame" x="0.0" y="0.0" width="208" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bsi-QS-utc">
<rect key="frame" x="208" y="0.0" width="24" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zwM-Iw-Hob" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="24.5" width="232" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bLL-8K-VWn">
<rect key="frame" x="0.0" y="49" width="232" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ybA-ob-sHe" customClass="StatusPollView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="73.5" width="232" height="6.5"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</subviews>
</stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark.square.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cqi-cV-ejs">
<rect key="frame" x="34" y="10" width="30" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="E9e-iF-rqo"/>
<constraint firstAttribute="width" constant="30" id="Efu-VP-pjH"/>
</constraints>
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="large"/>
</imageView>
</subviews>
<constraints>
<constraint firstItem="cqi-cV-ejs" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="34" id="8hN-WG-IsT"/>
<constraint firstAttribute="bottomMargin" secondItem="eSw-Oo-Scy" secondAttribute="bottom" id="9Hx-wD-Rfx"/>
<constraint firstItem="eSw-Oo-Scy" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="CxC-Ch-JAx"/>
<constraint firstAttribute="trailingMargin" secondItem="eSw-Oo-Scy" secondAttribute="trailing" id="OPc-Wi-cHD"/>
<constraint firstItem="cqi-cV-ejs" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="jQZ-cM-UuM"/>
<constraint firstItem="eSw-Oo-Scy" firstAttribute="leading" secondItem="cqi-cV-ejs" secondAttribute="trailing" constant="8" id="wMo-VG-1DK"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="displayNameLabel" destination="zwM-Iw-Hob" id="3VF-5X-B94"/>
<outlet property="pollView" destination="ybA-ob-sHe" id="lpi-94-dvu"/>
<outlet property="statusContentLabel" destination="bLL-8K-VWn" id="GZo-ko-eaD"/>
<outlet property="timestampLabel" destination="Bsi-QS-utc" id="ufI-re-iM2"/>
</connections>
<point key="canvasLocation" x="-62.318840579710148" y="-22.767857142857142"/>
</tableViewCell>
</objects>
<resources>
<image name="checkmark.square.fill" catalog="system" width="128" height="114"/>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Unknown Notification" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BC8-kp-x12">
<rect key="frame" x="16" y="11.5" width="164" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="BC8-kp-x12" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="MQf-82-GMw"/>
<constraint firstItem="BC8-kp-x12" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="gHv-BQ-CXd"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<point key="canvasLocation" x="132" y="154"/>
</tableViewCell>
</objects>
</document>

View File

@ -68,7 +68,7 @@ class PollOptionCheckboxView: UIView {
layer.borderColor = UIColor.gray.cgColor layer.borderColor = UIColor.gray.cgColor
} }
backgroundColor = isChecked && !voted ? tintColor : .clear backgroundColor = isChecked && !voted ? tintColor : .clear
imageView.tintColor = voted ? .black : .white imageView.tintColor = voted ? .label : .white
} }
} }

View File

@ -45,7 +45,7 @@ class PollOptionView: UIView {
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
fillView.leadingAnchor.constraint(equalTo: leadingAnchor), fillView.leadingAnchor.constraint(equalTo: leadingAnchor),
fillView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: CGFloat(optionVotes) / CGFloat(poll.votesCount)), fillView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: poll.votesCount == 0 ? 0 : CGFloat(optionVotes) / CGFloat(poll.votesCount)),
fillView.topAnchor.constraint(equalTo: topAnchor), fillView.topAnchor.constraint(equalTo: topAnchor),
fillView.bottomAnchor.constraint(equalTo: bottomAnchor), fillView.bottomAnchor.constraint(equalTo: bottomAnchor),
]) ])

View File

@ -15,7 +15,7 @@ protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
} }
class BaseStatusTableViewCell: UITableViewCell { class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
weak var delegate: StatusTableViewCellDelegate? { weak var delegate: StatusTableViewCellDelegate? {
didSet { didSet {
@ -338,6 +338,15 @@ class BaseStatusTableViewCell: UITableViewCell {
attachmentsView.attachmentViews.allObjects.forEach { $0.removeFromSuperview() } attachmentsView.attachmentViews.allObjects.forEach { $0.removeFromSuperview() }
showStatusAutomatically = false showStatusAutomatically = false
} }
// MARK: - MenuPreviewProvider
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return nil
}
// MARK: - Interaction
@IBAction func collapseButtonPressed() { @IBAction func collapseButtonPressed() {
setCollapsed(!collapsed, animated: true) setCollapsed(!collapsed, animated: true)
@ -457,10 +466,6 @@ class BaseStatusTableViewCell: UITableViewCell {
@objc func accountPressed() { @objc func accountPressed() {
delegate?.selected(account: accountID) delegate?.selected(account: accountID)
} }
func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return nil
}
} }
extension BaseStatusTableViewCell: AttachmentViewDelegate { extension BaseStatusTableViewCell: AttachmentViewDelegate {
@ -535,21 +540,6 @@ extension BaseStatusTableViewCell: AVPlayerViewControllerDelegate {
} }
} }
extension BaseStatusTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
if avatarImageView.frame.contains(location) {
return (
content: { ProfileViewController(accountID: self.accountID, mastodonController: mastodonController) },
actions: { self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) }
)
}
return self.getStatusCellPreviewProviders(for: location, sourceViewController: sourceViewController)
}
}
extension BaseStatusTableViewCell: UIDragInteractionDelegate { extension BaseStatusTableViewCell: UIDragInteractionDelegate {
func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let currentAccountID = mastodonController.accountInfo?.id, guard let currentAccountID = mastodonController.accountInfo?.id,

View File

@ -36,6 +36,8 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
accessibilityElements = [profileAccessibilityElement!, contentWarningLabel!, collapseButton!, contentTextView!, totalFavoritesButton!, totalReblogsButton!, timestampAndClientLabel!, replyButton!, favoriteButton!, reblogButton!, moreButton!] accessibilityElements = [profileAccessibilityElement!, contentWarningLabel!, collapseButton!, contentTextView!, totalFavoritesButton!, totalReblogsButton!, timestampAndClientLabel!, replyButton!, favoriteButton!, reblogButton!, moreButton!]
contentTextView.defaultFont = .systemFont(ofSize: 18) contentTextView.defaultFont = .systemFont(ofSize: 18)
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
} }
override func doUpdateUI(status: StatusMO, state: StatusState) { override func doUpdateUI(status: StatusMO, state: StatusState) {
@ -86,3 +88,13 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
} }
} }
} }
extension ConversationMainStatusTableViewCell: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
return UIContextMenuConfiguration(identifier: nil) {
ProfileViewController(accountID: self.accountID, mastodonController: self.mastodonController)
} actionProvider: { (_) in
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView))
}
}
}

View File

@ -50,6 +50,8 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true replyButton.imageView!.leadingAnchor.constraint(equalTo: contentTextView.leadingAnchor).isActive = true
contentTextView.defaultFont = .systemFont(ofSize: 16) contentTextView.defaultFont = .systemFont(ofSize: 16)
avatarImageView.addInteraction(UIContextMenuInteraction(delegate: self))
} }
override func createObserversIfNecessary() { override func createObserversIfNecessary() {
@ -181,7 +183,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
reply() reply()
} }
override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? { override func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
guard let mastodonController = mastodonController, guard let mastodonController = mastodonController,
let status = mastodonController.persistentContainer.status(for: statusID) else { let status = mastodonController.persistentContainer.status(for: statusID) else {
return nil return nil
@ -303,3 +305,13 @@ extension TimelineStatusTableViewCell: DraggableTableViewCell {
return [UIDragItem(itemProvider: provider)] return [UIDragItem(itemProvider: provider)]
} }
} }
extension TimelineStatusTableViewCell: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
return UIContextMenuConfiguration(identifier: nil) {
ProfileViewController(accountID: self.accountID, mastodonController: self.mastodonController)
} actionProvider: { (_) in
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView))
}
}
}