Favorites and reblogs
This commit is contained in:
parent
186e2d7520
commit
d4a451fadb
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg1007"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="Favorite.svg">
|
||||
<defs
|
||||
id="defs1001" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="293.05888"
|
||||
inkscape:cy="341.92599"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1395"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata1004">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.64583325;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path1571"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="68.784126"
|
||||
sodipodi:cy="238.46798"
|
||||
sodipodi:r1="72.331841"
|
||||
sodipodi:r2="30.379374"
|
||||
sodipodi:arg1="0.94281504"
|
||||
sodipodi:arg2="1.5711336"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 111.27998,297.00001 68.77388,268.84736 26.248805,296.97133 39.888461,247.84598 -6.6077817e-8,216.09302 50.935869,213.88454 68.80852,166.13615 l 17.840442,47.76043 50.934368,2.24284 -39.90987,31.72605 z"
|
||||
inkscape:transform-center-x="-0.0075377331"
|
||||
inkscape:transform-center-y="-6.8999101" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="793.70081"
|
||||
height="1122.5197"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg886"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="Reblog.svg"
|
||||
enable-background="new">
|
||||
<defs
|
||||
id="defs880">
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter2010">
|
||||
<feBlend
|
||||
inkscape:collect="always"
|
||||
mode="multiply"
|
||||
in2="BackgroundImage"
|
||||
id="feBlend2012" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.12"
|
||||
inkscape:cx="426.6819"
|
||||
inkscape:cy="222.48454"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1395"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:lockguides="false"
|
||||
inkscape:pagecheckerboard="false">
|
||||
<sodipodi:guide
|
||||
position="100.46678,110.57587"
|
||||
orientation="0,1"
|
||||
id="guide1767"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="39.817226,99.415325"
|
||||
orientation="1,0"
|
||||
id="guide1773"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="107.95499,89.359277"
|
||||
orientation="0,1"
|
||||
id="guide1987"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="79.634452,79.589293"
|
||||
orientation="-0.70563567,-0.70857484"
|
||||
id="guide1999"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="70.763562,8.6713087"
|
||||
orientation="0,1"
|
||||
id="guide904"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata883">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="opacity:0.92000002;filter:url(#filter2010)">
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g1956"
|
||||
id="use1958"
|
||||
transform="rotate(180,99.736921,237.37932)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<g
|
||||
id="g1956"
|
||||
transform="matrix(0.81315884,0,0,0.80688617,22.426927,19.068033)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:17.18317604;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
d="M 150.49023 671.84375 L 0 821.70898 L 109.83203 821.70898 L 109.83203 1009.0449 L 109.84961 1009.0449 C 109.83969 1009.0449 109.83203 1009.0527 109.83203 1009.0625 L 109.83203 1089.7285 C 109.83203 1089.7384 109.83969 1089.7461 109.84961 1089.7461 L 316.4707 1089.7461 C 316.47321 1089.7501 316.47547 1089.7559 316.48047 1089.7559 L 468.42773 1089.752 L 387.38867 1009.0508 L 316.49805 1009.0508 C 316.49539 1009.0492 316.49549 1009.0449 316.49219 1009.0449 L 191.14844 1009.0449 L 191.14844 821.70898 L 300.98047 821.70898 L 150.49023 671.84375 z "
|
||||
transform="matrix(0.32537718,0,0,0.32790664,-27.580008,-23.631627)"
|
||||
id="path1716" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,13 @@
|
|||
import UIKit
|
||||
|
||||
func test(_ nillable: String?) {
|
||||
defer {
|
||||
print("defer")
|
||||
}
|
||||
guard let value = nillable else { return }
|
||||
print(value)
|
||||
}
|
||||
|
||||
test("test")
|
||||
print("------")
|
||||
test(nil)
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
|
||||
<timeline fileName='timeline.xctimeline'/>
|
||||
</playground>
|
|
@ -798,6 +798,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
|
@ -807,6 +808,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Tusker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
@ -816,6 +818,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
|
@ -825,6 +828,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Tusker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:MyPlayground.playground">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "container:Tusker.xcodeproj">
|
||||
</FileRef>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Favorite.pdf",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template",
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Reblog.pdf",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template",
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -77,6 +77,9 @@ extension StatusTableViewCellDelegate where Self: UIViewController {
|
|||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
func updatedStatus(for cell: StatusTableViewCell, status: Status) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LargeImageViewControllerDelegate where Self: UIViewController {
|
||||
|
|
|
@ -133,5 +133,10 @@ class TimelineTableViewController: UITableViewController {
|
|||
|
||||
}
|
||||
|
||||
extension TimelineTableViewController: StatusTableViewCellDelegate {}
|
||||
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||
func updatedStatus(for cell: StatusTableViewCell, status: Status) {
|
||||
let indexPath = tableView.indexPath(for: cell)!
|
||||
statuses[indexPath.row] = status
|
||||
}
|
||||
}
|
||||
extension TimelineTableViewController: LargeImageViewControllerDelegate {}
|
||||
|
|
|
@ -19,12 +19,24 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
|||
@IBOutlet weak var avatarImageView: UIImageView!
|
||||
@IBOutlet weak var timestampLabel: UILabel!
|
||||
@IBOutlet weak var attachmentsView: UIView!
|
||||
@IBOutlet weak var favoriteButton: UIButton!
|
||||
@IBOutlet weak var reblogButton: UIButton!
|
||||
|
||||
var status: Status!
|
||||
var account: Account!
|
||||
|
||||
var avatarURL: URL?
|
||||
var favorited: Bool = false {
|
||||
didSet {
|
||||
favoriteButton.tintColor = favorited ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||
}
|
||||
}
|
||||
var reblogged: Bool = false {
|
||||
didSet {
|
||||
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||
}
|
||||
}
|
||||
|
||||
var avatarURL: URL?
|
||||
var updateTimestampWorkItem: DispatchWorkItem?
|
||||
|
||||
override func awakeFromNib() {
|
||||
|
@ -37,6 +49,7 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
|||
avatarImageView.layer.masksToBounds = true
|
||||
attachmentsView.layer.cornerRadius = 5
|
||||
attachmentsView.layer.masksToBounds = true
|
||||
contentLabel.delegate = self
|
||||
}
|
||||
|
||||
func updateUIForPreferences() {
|
||||
|
@ -97,8 +110,11 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
|||
attachmentsView.isHidden = true
|
||||
}
|
||||
|
||||
let realStatus = status.reblog ?? status
|
||||
favorited = realStatus.favourited ?? false
|
||||
reblogged = realStatus.reblogged ?? false
|
||||
|
||||
contentLabel.status = status
|
||||
contentLabel.delegate = self
|
||||
}
|
||||
|
||||
func updateTimestamp() {
|
||||
|
@ -147,6 +163,60 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
|
|||
delegate?.reply(to: status)
|
||||
}
|
||||
|
||||
@IBAction func favoritePressed(_ sender: Any) {
|
||||
let oldValue = favorited
|
||||
favorited = !favorited
|
||||
|
||||
let realStatus: Status = status.reblog ?? status
|
||||
let req = favorited ? Statuses.favourite(id: realStatus.id) : Statuses.unfavourite(id: realStatus.id)
|
||||
|
||||
MastodonController.shared.client.run(req) { result in
|
||||
guard case .success = result else {
|
||||
print("Couldn't favorite status \(realStatus.id)")
|
||||
// todo: display error message
|
||||
DispatchQueue.main.async {
|
||||
self.favorited = oldValue
|
||||
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||
generator.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func reblogPressed(_ sender: Any) {
|
||||
let oldValue = reblogged
|
||||
reblogged = !reblogged
|
||||
|
||||
let realStatus: Status = status.reblog ?? status
|
||||
let req = reblogged ? Statuses.reblog(id: realStatus.id) : Statuses.unreblog(id: realStatus.id)
|
||||
|
||||
MastodonController.shared.client.run(req) { result in
|
||||
guard case .success = result else {
|
||||
print("Couldn't reblog status \(realStatus.id)")
|
||||
// todo: display error message
|
||||
DispatchQueue.main.async {
|
||||
self.reblogged = oldValue
|
||||
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||
generator.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ConversationMainStatusTableViewCell: HTMLContentLabelDelegate {
|
||||
|
|
|
@ -79,21 +79,43 @@
|
|||
<constraint firstAttribute="height" priority="999" constant="200" id="UMv-Bk-ZyY"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
|
||||
<rect key="frame" x="0.0" y="164" width="27" height="20"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
|
||||
<rect key="frame" x="0.0" y="164" width="121.5" height="20"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2cc-lE-AdG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="27" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="2cc-lE-AdG" secondAttribute="height" multiplier="205:151" id="1Nc-Ix-kYQ"/>
|
||||
<constraint firstAttribute="height" constant="20" id="TP1-IY-Xk2"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Reply"/>
|
||||
<connections>
|
||||
<action selector="replyPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="hsh-gx-Swo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DhN-rJ-jdA">
|
||||
<rect key="frame" x="47" y="0.0" width="21" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="DhN-rJ-jdA" secondAttribute="height" multiplier="137:131" id="POd-P3-n6S"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Favorite"/>
|
||||
<connections>
|
||||
<action selector="favoritePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="Hkh-Zo-9Qu"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GUG-f7-Hdy">
|
||||
<rect key="frame" x="88" y="0.0" width="33.5" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="GUG-f7-Hdy" secondAttribute="height" multiplier="927:558" id="V8H-aT-eIJ"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Reblog"/>
|
||||
<connections>
|
||||
<action selector="reblogPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="SAf-RN-q8N"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="bqe-m8-5Lo"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
|
@ -116,6 +138,8 @@
|
|||
<outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/>
|
||||
<outlet property="contentLabel" destination="TgY-hs-Klo" id="SEi-B2-VQf"/>
|
||||
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
|
||||
<outlet property="favoriteButton" destination="DhN-rJ-jdA" id="b2Q-ch-kSP"/>
|
||||
<outlet property="reblogButton" destination="GUG-f7-Hdy" id="WtT-Ph-DQm"/>
|
||||
<outlet property="timestampLabel" destination="R3N-Pg-4hn" id="z5j-Tw-Lk5"/>
|
||||
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
|
||||
</connections>
|
||||
|
@ -123,6 +147,8 @@
|
|||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Favorite" width="390" height="371"/>
|
||||
<image name="Reblog" width="679" height="406"/>
|
||||
<image name="Reply" width="155" height="114"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -25,6 +25,8 @@ protocol StatusTableViewCellDelegate {
|
|||
|
||||
func showLargeImage(_ image: UIImage, description: String?, animatingFrom originView: UIView)
|
||||
|
||||
func updatedStatus(for cell: StatusTableViewCell, status: Status)
|
||||
|
||||
}
|
||||
|
||||
class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
||||
|
@ -38,11 +40,24 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
@IBOutlet weak var reblogLabel: UILabel!
|
||||
@IBOutlet weak var timestampLabel: UILabel!
|
||||
@IBOutlet weak var attachmentsView: UIView!
|
||||
@IBOutlet weak var favoriteButton: UIButton!
|
||||
@IBOutlet weak var reblogButton: UIButton!
|
||||
|
||||
var status: Status!
|
||||
var account: Account!
|
||||
var reblogger: Account?
|
||||
|
||||
var favorited: Bool = false {
|
||||
didSet {
|
||||
favoriteButton.tintColor = favorited ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||
}
|
||||
}
|
||||
var reblogged: Bool = false {
|
||||
didSet {
|
||||
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||
}
|
||||
}
|
||||
|
||||
var avatarURL: URL?
|
||||
var updateTimestampWorkItem: DispatchWorkItem?
|
||||
var attachmentDataTasks: [URLSessionDataTask] = []
|
||||
|
@ -129,6 +144,10 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
|
||||
}
|
||||
|
||||
let realStatus = status.reblog ?? status
|
||||
favorited = realStatus.favourited ?? false
|
||||
reblogged = realStatus.reblogged ?? false
|
||||
|
||||
contentLabel.status = status
|
||||
}
|
||||
|
||||
|
@ -192,6 +211,60 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
|
|||
delegate?.selected(account: reblogger)
|
||||
}
|
||||
|
||||
@IBAction func favoritePressed(_ sender: Any) {
|
||||
let oldValue = favorited
|
||||
favorited = !favorited
|
||||
|
||||
let realStatus: Status = status.reblog ?? status
|
||||
let req = favorited ? Statuses.favourite(id: realStatus.id) : Statuses.unfavourite(id: realStatus.id)
|
||||
|
||||
MastodonController.shared.client.run(req) { result in
|
||||
guard case .success = result else {
|
||||
print("Couldn't favorite status \(realStatus.id)")
|
||||
// todo: display error message
|
||||
DispatchQueue.main.async {
|
||||
self.favorited = oldValue
|
||||
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||
generator.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func reblogPressed(_ sender: Any) {
|
||||
let oldValue = reblogged
|
||||
reblogged = !reblogged
|
||||
|
||||
let realStatus: Status = status.reblog ?? status
|
||||
let req = reblogged ? Statuses.reblog(id: realStatus.id) : Statuses.unreblog(id: realStatus.id)
|
||||
|
||||
MastodonController.shared.client.run(req) { result in
|
||||
guard case .success = result else {
|
||||
print("Couldn't reblog status \(realStatus.id)")
|
||||
// todo: display error message
|
||||
DispatchQueue.main.async {
|
||||
self.reblogged = oldValue
|
||||
|
||||
let generator = UINotificationFeedbackGenerator()
|
||||
generator.notificationOccurred(.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||
generator.impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusTableViewCell: HTMLContentLabelDelegate {
|
||||
|
|
|
@ -86,13 +86,12 @@
|
|||
<constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
||||
<rect key="frame" x="0.0" y="119" width="20.5" height="15"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
|
||||
<rect key="frame" x="0.0" y="119" width="91" height="15"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
|
||||
<rect key="frame" x="0.0" y="0.0" width="20.5" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="15" id="OOm-BC-mVD"/>
|
||||
<constraint firstAttribute="width" secondItem="rKF-yF-KIa" secondAttribute="height" multiplier="205:151" id="eCt-en-YfI"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Reply"/>
|
||||
|
@ -100,7 +99,30 @@
|
|||
<action selector="replyPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="Ohg-uU-d3Z"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
||||
<rect key="frame" x="35.5" y="0.0" width="15.5" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="x0t-TR-jJ4" secondAttribute="height" multiplier="137:131" id="MRT-Ae-MgD"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Favorite"/>
|
||||
<connections>
|
||||
<action selector="favoritePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="gKJ-Hu-za3"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
||||
<rect key="frame" x="66" y="0.0" width="25" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="6tW-z8-Qh9" secondAttribute="height" multiplier="927:558" id="lV9-JI-Q4e"/>
|
||||
</constraints>
|
||||
<state key="normal" image="Reblog"/>
|
||||
<connections>
|
||||
<action selector="reblogPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="JQI-VT-wTt"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="15" id="TKC-Uc-C4K"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
|
@ -123,6 +145,8 @@
|
|||
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="CAl-hK-i3j"/>
|
||||
<outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
|
||||
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
|
||||
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/>
|
||||
<outlet property="reblogButton" destination="6tW-z8-Qh9" id="i9h-QA-ZPd"/>
|
||||
<outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
|
||||
<outlet property="timestampLabel" destination="35d-EA-ReR" id="8EW-mb-LAb"/>
|
||||
<outlet property="usernameLabel" destination="j89-zc-SFa" id="see-Xd-3e9"/>
|
||||
|
@ -131,6 +155,8 @@
|
|||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Favorite" width="390" height="371"/>
|
||||
<image name="Reblog" width="679" height="406"/>
|
||||
<image name="Reply" width="155" height="114"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
Loading…
Reference in New Issue