From 978f8d2d5ef909d18bb8bf53fdebf7e74be9d366 Mon Sep 17 00:00:00 2001
From: kkostiuk <kateryna.kostiuk@savoirfairelinux.com>
Date: Wed, 21 Apr 2021 11:38:18 -0400
Subject: [PATCH] conversation: actions for interactions

1. enable image zoom
2. update actions for interactions:
- resend: for failed interactions
- preview: for completed file transfers
- share: for completed file transfers
- forward: for completed transfer and for all messages
- save to gallery: for completed image transfers

Change-Id: Ie31f745d52780229bf3260a358af8f6e0279f2a0
---
 .../ContactPickerViewController.storyboard    |  24 ++--
 .../ContactPickerViewController.swift         |   3 +
 Ring/Ring/Constants/Generated/Images.swift    |   3 +
 Ring/Ring/Constants/Generated/Strings.swift   |  10 ++
 .../Conversation/Cells/MessageCell.swift      |  90 ++++++++++--
 .../ConversationViewController.swift          | 136 ++++++++++++++----
 .../Conversation/ConversationViewModel.swift  |  34 ++++-
 .../Conversation/MessageViewModel.swift       |   1 +
 .../Preview/PreviewViewController.storyboard  |  66 ++++++++-
 .../Preview/PreviewViewController.swift       |  75 ++++++++++
 .../Protocols/ConversationNavigation.swift    |   9 +-
 .../Resources/Images.xcassets/Contents.json   |   6 +-
 .../Contents.json                             |  16 ++-
 .../delete-1.png                              | Bin 391 -> 0 bytes
 .../delete-2.png                              | Bin 272 -> 0 bytes
 .../delete.png                                | Bin 306 -> 0 bytes
 .../trash-can-outline-2.png                   | Bin 0 -> 562 bytes
 .../trash-can-outline-3.png                   | Bin 0 -> 769 bytes
 .../trash-can-outline.png                     | Bin 0 -> 481 bytes
 .../ic_forward.imageset/Contents.json         |  23 +++
 .../ic_forward.imageset/share-outline-2.png   | Bin 0 -> 834 bytes
 .../ic_forward.imageset/share-outline-4.png   | Bin 0 -> 1188 bytes
 .../ic_forward.imageset/share-outline.png     | Bin 0 -> 547 bytes
 .../ic_save.imageset/Contents.json            |  26 ++++
 .../ic_save.imageset/tray-arrow-down-2.png    | Bin 0 -> 760 bytes
 .../ic_save.imageset/tray-arrow-down-4.png    | Bin 0 -> 1092 bytes
 .../ic_save.imageset/tray-arrow-down.png      | Bin 0 -> 546 bytes
 .../ic_share.imageset/Contents.json           |  23 +++
 .../ic_share.imageset/export-variant-3.png    | Bin 0 -> 386 bytes
 .../ic_share.imageset/export-variant-4.png    | Bin 0 -> 557 bytes
 .../ic_share.imageset/export-variant.png      | Bin 0 -> 713 bytes
 .../Resources/en.lproj/Localizable.strings    |   5 +
 32 files changed, 476 insertions(+), 74 deletions(-)
 delete mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-1.png
 delete mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-2.png
 delete mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-2.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-3.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/Contents.json
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-2.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-4.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_save.imageset/Contents.json
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-2.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-4.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_share.imageset/Contents.json
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-3.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-4.png
 create mode 100644 Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant.png

diff --git a/Ring/Ring/Calls/Conference/ContactPickerViewController.storyboard b/Ring/Ring/Calls/Conference/ContactPickerViewController.storyboard
index 2e3931788..b709a859d 100644
--- a/Ring/Ring/Calls/Conference/ContactPickerViewController.storyboard
+++ b/Ring/Ring/Calls/Conference/ContactPickerViewController.storyboard
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ZwP-Qn-oLY">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ZwP-Qn-oLY">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -11,7 +11,7 @@
         <!--Contact Picker View Controller-->
         <scene sceneID="QZd-Vi-EyD">
             <objects>
-                <viewController modalPresentationStyle="overCurrentContext" id="ZwP-Qn-oLY" customClass="ContactPickerViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController extendedLayoutIncludesOpaqueBars="YES" modalPresentationStyle="overFullScreen" id="ZwP-Qn-oLY" customClass="ContactPickerViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" contentMode="scaleToFill" id="TtT-WG-OAE">
                         <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -40,10 +40,16 @@
                             <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Bdz-kj-0K4">
                                 <rect key="frame" x="0.0" y="44" width="414" height="818"/>
                                 <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZ0-vA-uc5">
+                                        <rect key="frame" x="182" y="0.0" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="50" id="C1b-Mo-dnW"/>
+                                        </constraints>
+                                    </view>
                                     <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cfK-DS-kt1">
-                                        <rect key="frame" x="0.0" y="0.0" width="414" height="90"/>
+                                        <rect key="frame" x="0.0" y="50" width="414" height="90"/>
                                         <subviews>
-                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6AM-a0-weG">
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6AM-a0-weG">
                                                 <rect key="frame" x="364" y="45" width="30" height="35"/>
                                                 <fontDescription key="fontDescription" type="system" pointSize="19"/>
                                             </button>
@@ -55,7 +61,7 @@
                                         </constraints>
                                     </view>
                                     <searchBar contentMode="redraw" searchBarStyle="prominent" showsSearchResultsButton="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ht5-JP-L4t">
-                                        <rect key="frame" x="0.0" y="90" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="140" width="414" height="44"/>
                                         <constraints>
                                             <constraint firstAttribute="height" constant="44" id="Ott-Go-5mf"/>
                                         </constraints>
@@ -63,7 +69,7 @@
                                         <textInputTraits key="textInputTraits"/>
                                     </searchBar>
                                     <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsMultipleSelection="YES" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="i1P-Li-H82">
-                                        <rect key="frame" x="0.0" y="134" width="414" height="684"/>
+                                        <rect key="frame" x="0.0" y="184" width="414" height="634"/>
                                         <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                         <connections>
                                             <outlet property="delegate" destination="ZwP-Qn-oLY" id="EoQ-WF-bN4"/>
@@ -71,7 +77,6 @@
                                     </tableView>
                                 </subviews>
                                 <constraints>
-                                    <constraint firstItem="cfK-DS-kt1" firstAttribute="top" secondItem="Bdz-kj-0K4" secondAttribute="top" id="4Ai-6C-pS6"/>
                                     <constraint firstAttribute="trailing" secondItem="i1P-Li-H82" secondAttribute="trailing" id="4SM-vM-mgc"/>
                                     <constraint firstAttribute="bottom" secondItem="i1P-Li-H82" secondAttribute="bottom" id="MtY-d8-Qac"/>
                                     <constraint firstItem="cfK-DS-kt1" firstAttribute="leading" secondItem="Bdz-kj-0K4" secondAttribute="leading" id="RVA-PE-P1b"/>
@@ -82,6 +87,7 @@
                                 </constraints>
                             </stackView>
                         </subviews>
+                        <viewLayoutGuide key="safeArea" id="HLr-8o-AJK"/>
                         <constraints>
                             <constraint firstItem="VI3-Wm-odB" firstAttribute="top" secondItem="Bdz-kj-0K4" secondAttribute="top" id="0qq-2Q-k11"/>
                             <constraint firstItem="VI3-Wm-odB" firstAttribute="leading" secondItem="Bdz-kj-0K4" secondAttribute="leading" id="5pC-zY-2Vk"/>
@@ -92,12 +98,12 @@
                             <constraint firstItem="HLr-8o-AJK" firstAttribute="bottom" secondItem="VI3-Wm-odB" secondAttribute="bottom" id="l3e-iB-s2n"/>
                             <constraint firstItem="VI3-Wm-odB" firstAttribute="trailing" secondItem="Bdz-kj-0K4" secondAttribute="trailing" id="sVI-Ny-tp3"/>
                         </constraints>
-                        <viewLayoutGuide key="safeArea" id="HLr-8o-AJK"/>
                     </view>
                     <connections>
                         <outlet property="doneButton" destination="6AM-a0-weG" id="2yX-Fh-qwe"/>
                         <outlet property="searchBar" destination="ht5-JP-L4t" id="adL-r1-B3M"/>
                         <outlet property="tableView" destination="i1P-Li-H82" id="Hbd-c3-3WV"/>
+                        <outlet property="topSpace" destination="C1b-Mo-dnW" id="0UO-Q0-o1H"/>
                         <outlet property="topViewContainer" destination="cfK-DS-kt1" id="s5H-k8-YXw"/>
                     </connections>
                 </viewController>
diff --git a/Ring/Ring/Calls/Conference/ContactPickerViewController.swift b/Ring/Ring/Calls/Conference/ContactPickerViewController.swift
index 8c85ad965..1ceca8c43 100644
--- a/Ring/Ring/Calls/Conference/ContactPickerViewController.swift
+++ b/Ring/Ring/Calls/Conference/ContactPickerViewController.swift
@@ -37,6 +37,7 @@ class ContactPickerViewController: UIViewController, StoryboardBased, ViewModelB
     @IBOutlet weak var tableView: UITableView!
     @IBOutlet weak var doneButton: UIButton!
     @IBOutlet weak var topViewContainer: UIView!
+    @IBOutlet weak var topSpace: NSLayoutConstraint!
 
     var viewModel: ContactPickerViewModel!
     private let disposeBag = DisposeBag()
@@ -224,6 +225,7 @@ class ContactPickerViewController: UIViewController, StoryboardBased, ViewModelB
             let dismissGR = UISwipeGestureRecognizer(target: self, action: #selector(remove(gesture:)))
             dismissGR.direction = UISwipeGestureRecognizer.Direction.down
             dismissGR.delegate = self
+            topSpace.constant = 0
             self.searchBar.addGestureRecognizer(dismissGR)
             self.rowSelectionHandler = { [weak self] row in
                 guard let self = self else { return }
@@ -236,6 +238,7 @@ class ContactPickerViewController: UIViewController, StoryboardBased, ViewModelB
             self.searchBar.backgroundColor = UIColor.clear
             self.doneButton.setTitle(L10n.Actions.cancelAction, for: .normal)
             self.doneButton.setTitleColor(UIColor.jamiTextBlue, for: .normal)
+            topSpace.constant = 50
             self.doneButton.rx.tap
                 .subscribe(onNext: { [weak self] in
                     let paths = self?.tableView.indexPathsForSelectedRows
diff --git a/Ring/Ring/Constants/Generated/Images.swift b/Ring/Ring/Constants/Generated/Images.swift
index f346b3137..adc0e7473 100644
--- a/Ring/Ring/Constants/Generated/Images.swift
+++ b/Ring/Ring/Constants/Generated/Images.swift
@@ -50,7 +50,10 @@ internal enum Asset {
   internal static let icBack = ImageAsset(name: "ic_back")
   internal static let icContactPicture = ImageAsset(name: "ic_contact_picture")
   internal static let icConversationRemove = ImageAsset(name: "ic_conversation_remove")
+  internal static let icForward = ImageAsset(name: "ic_forward")
   internal static let icHideInput = ImageAsset(name: "ic_hide_input")
+  internal static let icSave = ImageAsset(name: "ic_save")
+  internal static let icShare = ImageAsset(name: "ic_share")
   internal static let icShowInput = ImageAsset(name: "ic_show_input")
   internal static let infoArrow = ImageAsset(name: "info_arrow")
   internal static let jamiIcon = ImageAsset(name: "jamiIcon")
diff --git a/Ring/Ring/Constants/Generated/Strings.swift b/Ring/Ring/Constants/Generated/Strings.swift
index 532c2bb4e..17e9c272b 100644
--- a/Ring/Ring/Constants/Generated/Strings.swift
+++ b/Ring/Ring/Constants/Generated/Strings.swift
@@ -321,6 +321,8 @@ internal enum L10n {
   }
 
   internal enum Conversation {
+    /// Failed to save image to galery
+    internal static let errorSavingImage = L10n.tr("Localizable", "conversation.errorSavingImage")
     /// You are currently receiving a live location from 
     internal static let explanationReceivingLocationFrom = L10n.tr("Localizable", "conversation.explanationReceivingLocationFrom")
     /// You are currently sharing your location with 
@@ -451,12 +453,20 @@ internal enum L10n {
     internal static let close = L10n.tr("Localizable", "global.close")
     /// Invitations
     internal static let contactRequestsTabBarTitle = L10n.tr("Localizable", "global.contactRequestsTabBarTitle")
+    /// Forward
+    internal static let forward = L10n.tr("Localizable", "global.forward")
     /// Conversations
     internal static let homeTabBarTitle = L10n.tr("Localizable", "global.homeTabBarTitle")
     /// Account
     internal static let meTabBarTitle = L10n.tr("Localizable", "global.meTabBarTitle")
     /// Ok
     internal static let ok = L10n.tr("Localizable", "global.ok")
+    /// Preview
+    internal static let preview = L10n.tr("Localizable", "global.preview")
+    /// Resend
+    internal static let resend = L10n.tr("Localizable", "global.resend")
+    /// Save
+    internal static let save = L10n.tr("Localizable", "global.save")
     /// Share
     internal static let share = L10n.tr("Localizable", "global.share")
     /// Together
diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
index c54f98c38..36579cbc4 100644
--- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCell.swift
@@ -30,8 +30,7 @@ import SwiftyBeaver
 
 // swiftlint:disable type_body_length
 // swiftlint:disable file_length
-class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
-
+class MessageCell: UITableViewCell, NibReusable, PlayerDelegate, PreviewViewControllerDelegate {
     // MARK: Properties
 
     let log = SwiftyBeaver.self
@@ -76,10 +75,17 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
     private(set) var messageId: Int64?
     private var isCopyable: Bool = false
     private var couldBeShared: Bool = false
+    private var couldBeResend: Bool = false
+    private var couldBeForward: Bool = false
+    private var couldBeSaved: Bool = false
     private let _deleteMessage = BehaviorRelay<Bool>(value: false)
     var deleteMessage: Observable<Bool> { _deleteMessage.asObservable() }
 
     var shareMessage = PublishSubject<Bool>()
+    var forwardMessage = PublishSubject<Bool>()
+    var saveMessage = PublishSubject<Bool>()
+    var resendMessage = PublishSubject<Bool>()
+    var previewMessage = PublishSubject<Bool>()
 
     private let showTimeTap = BehaviorRelay<Bool>(value: false)
     var tappedToShowTime: Observable<Bool> { showTimeTap.asObservable() }
@@ -134,6 +140,8 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.messageId = nil
         self.isCopyable = false
         self.couldBeShared = false
+        self.couldBeResend = false
+        self.couldBeForward = false
         self._deleteMessage.accept(false)
         if let longGestureRecognizer = longGestureRecognizer {
             self.bubble.removeGestureRecognizer(longGestureRecognizer)
@@ -207,21 +215,17 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         }
     }
 
-    func supportFullScreenMode() -> Bool {
-        return (playerView != nil && playerView!.viewModel.hasVideo.value) || self.transferImageView.image != nil
-    }
-
     // MARK: Configure
 
     func configureTapGesture() {
-        let shownByDefault = !self.timeLabel.isHidden && !showTimeTap.value && !supportFullScreenMode()
+        let shownByDefault = !self.timeLabel.isHidden && !showTimeTap.value && !self.couldBeShared
         if shownByDefault { return }
         self.bubble.isUserInteractionEnabled = true
         self.tapGestureRecognizer = UITapGestureRecognizer()
         self.tapGestureRecognizer?.numberOfTapsRequired = 1
         self.tapGestureRecognizer?.delegate = self
         self.tapGestureRecognizer!.rx.event.bind(onNext: { [weak self] _ in self?.onTapGesture() }).disposed(by: self.disposeBag)
-        self.tapGestureRecognizer!.cancelsTouchesInView = supportFullScreenMode() ? true : false
+        self.tapGestureRecognizer!.cancelsTouchesInView = self.couldBeShared ? true : false
         self.bubble.addGestureRecognizer(tapGestureRecognizer!)
         guard let doubleTap = doubleTapGestureRecognizer else { return }
         self.tapGestureRecognizer?.require(toFail: doubleTap)
@@ -245,7 +249,7 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
 
     func onTapGesture() {
         // for player or image expand size on tap, for other messages show time
-        if supportFullScreenMode() {
+        if self.couldBeShared {
             openPreview.accept(true)
             return
         }
@@ -261,10 +265,13 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.showTimeTap.accept(true)
     }
 
-    private func configureLongGesture(_ messageId: Int64, _ bubblePosition: BubblePosition, _ isTransfer: Bool, _ isLocationSharingBubble: Bool) {
+    private func configureLongGesture(_ messageId: Int64, _ bubblePosition: BubblePosition, _ isTransfer: Bool, _ isLocationSharingBubble: Bool, isTransferSuccess: Bool, isError: Bool) {
         self.messageId = messageId
         self.isCopyable = bubblePosition != .generated && !isTransfer && !isLocationSharingBubble
-        self.couldBeShared = bubblePosition != .generated && !isLocationSharingBubble
+        self.couldBeShared = isTransfer && isTransferSuccess
+        self.couldBeForward = bubblePosition != .generated && !isLocationSharingBubble && (isTransferSuccess || !isTransfer)
+        self.couldBeResend = isError && bubblePosition == .sent
+        self.couldBeSaved = self.couldBeShared && self.transferedImage != nil
         self.bubble.isUserInteractionEnabled = true
         longGestureRecognizer = UILongPressGestureRecognizer()
         longGestureRecognizer!.rx.event.bind(onNext: { [weak self] _ in self?.onLongGesture() }).disposed(by: self.disposeBag)
@@ -275,7 +282,11 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         becomeFirstResponder()
         let menu = UIMenuController.shared
         let shareItem = UIMenuItem(title: L10n.Global.share, action: NSSelectorFromString("share"))
-        menu.menuItems = [shareItem]
+        let forwardItem = UIMenuItem(title: L10n.Global.forward, action: NSSelectorFromString("forward"))
+        let saveItem = UIMenuItem(title: L10n.Global.save, action: NSSelectorFromString("save"))
+        let resendItem = UIMenuItem(title: L10n.Global.resend, action: NSSelectorFromString("resend"))
+        let previewItem = UIMenuItem(title: L10n.Global.preview, action: NSSelectorFromString("preview"))
+        menu.menuItems = [previewItem, shareItem, forwardItem, saveItem, resendItem]
         if !menu.isMenuVisible {
             menu.setTargetRect(self.bubble.frame, in: self)
             menu.setMenuVisible(true, animated: true)
@@ -287,6 +298,42 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         shareMessage.onNext(true)
     }
 
+    @objc
+    func forward() {
+        forwardMessage.onNext(true)
+    }
+
+    @objc
+    func save() {
+        saveMessage.onNext(true)
+    }
+
+    @objc
+    func resend() {
+        resendMessage.onNext(true)
+    }
+
+    @objc
+    func preview() {
+        previewMessage.onNext(true)
+    }
+
+    func deleteFile() {
+        _deleteMessage.accept(true)
+    }
+
+    func shareFile() {
+        shareMessage.onNext(true)
+    }
+
+    func forwardFile() {
+        forwardMessage.onNext(true)
+    }
+
+    func saveFile() {
+        saveMessage.onNext(true)
+    }
+
     override func copy(_ sender: Any?) {
         UIPasteboard.general.string = self.messageLabel?.text
         UIMenuController.shared.setMenuVisible(false, animated: true)
@@ -302,7 +349,12 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
 
     override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
         return action == #selector(UIResponderStandardEditActions.copy) && self.isCopyable ||
-            action == #selector(UIResponderStandardEditActions.delete) || (action == NSSelectorFromString("share") && self.couldBeShared)
+            action == #selector(UIResponderStandardEditActions.delete) ||
+            (action == NSSelectorFromString("forward") && self.couldBeForward) ||
+            (action == NSSelectorFromString("share") && self.couldBeShared) ||
+            (action == NSSelectorFromString("resend") && self.couldBeResend) ||
+            (action == NSSelectorFromString("save") && self.couldBeSaved) ||
+            (action == NSSelectorFromString("preview") && self.couldBeShared)
     }
 
     func toggleCellTimeLabelVisibility() {
@@ -441,7 +493,6 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         self.configureCellTimeLabel(item)
 
         self.prepareForReuseLongGesture()
-        self.configureLongGesture(item.message.messageId, item.bubblePosition(), item.isTransfer, item.isLocationSharingBubble)
 
         self.prepareForReuseTapGesture()
 
@@ -522,7 +573,16 @@ class MessageCell: UITableViewCell, NibReusable, PlayerDelegate {
         } else if item.isTransfer {
             self.messageLabelMarginConstraint.constant = -2
         }
-
+        var isError = false
+        if item.isTransfer {
+            isError = item.initialTransferStatus == .error || item.initialTransferStatus == .canceled
+        } else if item.isText {
+            isError = item.message.status == .failure
+        }
+        self.configureLongGesture(item.message.messageId, item.bubblePosition(),
+                                  item.isTransfer, item.isLocationSharingBubble,
+                                  isTransferSuccess: item.initialTransferStatus == .success,
+                                  isError: isError)
         self.configureTapGesture()
     }
 
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
index e55a3dd70..efc268fd3 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
@@ -34,7 +34,9 @@ import MobileCoreServices
 // swiftlint:disable type_body_length
 class ConversationViewController: UIViewController,
                                   UIImagePickerControllerDelegate, UINavigationControllerDelegate,
-                                  UIDocumentPickerDelegate, StoryboardBased, ViewModelBased, MessageAccessoryViewDelegate, ContactPickerDelegate, PHPickerViewControllerDelegate {
+                                  UIDocumentPickerDelegate, StoryboardBased, ViewModelBased,
+                                  MessageAccessoryViewDelegate, ContactPickerDelegate,
+                                  PHPickerViewControllerDelegate, UIDocumentInteractionControllerDelegate {
 
     let log = SwiftyBeaver.self
 
@@ -73,18 +75,6 @@ class ConversationViewController: UIViewController,
         self.setupUI()
         self.setupTableView()
         self.setupBindings()
-        NotificationCenter.default.rx
-            .notification(UIDevice.orientationDidChangeNotification)
-            .observeOn(MainScheduler.instance)
-            .subscribe(onNext: {[weak self](_) in
-                guard let self = self,
-                UIDevice.current.portraitOrLandscape else { return }
-                self.setupNavTitle(profileImageData: self.viewModel.profileImageData.value,
-                                   displayName: self.viewModel.displayName.value,
-                                   username: self.viewModel.userName.value)
-                self.tableView.reloadData()
-            })
-            .disposed(by: self.disposeBag)
 
         /*
          Register to keyboard notifications to adjust tableView insets when the keybaord appears
@@ -100,6 +90,22 @@ class ConversationViewController: UIViewController,
         keyboardDismissTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
     }
 
+    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+        // Waiting for screen size change
+        DispatchQueue.global(qos: .background).async {
+            sleep(UInt32(0.5))
+            DispatchQueue.main.async { [weak self] in
+                guard let self = self,
+                UIDevice.current.portraitOrLandscape else { return }
+                self.setupNavTitle(profileImageData: self.viewModel.profileImageData.value,
+                                   displayName: self.viewModel.displayName.value,
+                                   username: self.viewModel.userName.value)
+                self.tableView.reloadData()
+            }
+        }
+        super.viewWillTransition(to: size, with: coordinator)
+    }
+
     @objc
     private func applicationWillResignActive() {
         self.viewModel.setIsComposingMsg(isComposing: false)
@@ -137,6 +143,8 @@ class ConversationViewController: UIViewController,
         self.present(alert, animated: true, completion: nil)
     }
 
+    // MARK: photo library
+
     func checkPhotoLibraryPermission() {
         let status = PHPhotoLibrary.authorizationStatus()
         switch status {
@@ -367,6 +375,19 @@ class ConversationViewController: UIViewController,
             })
     }
 
+    func saveImageToGalery (image: UIImage) {
+        UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
+    }
+
+    @objc
+    func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
+        if let error = error {
+            let allert = UIAlertController(title: L10n.Conversation.errorSavingImage, message: error.localizedDescription, preferredStyle: .alert)
+            allert.addAction(UIAlertAction(title: "OK", style: .default))
+            present(allert, animated: true)
+        }
+    }
+
     @objc
     func dismissKeyboard() {
         self.becomeFirstResponder()
@@ -954,18 +975,38 @@ class ConversationViewController: UIViewController,
             .disposed(by: cell.disposeBag)
     }
 
+    // MARK: open file
+
+    func openDocument(messageModel: MessageViewModel) {
+        let conversation = self.viewModel.conversation.value.conversationId
+        let accountId = self.viewModel.conversation.value.accountId
+        guard let url = messageModel.transferedFile(conversationID: conversation, accountId: accountId),
+              FileManager().fileExists(atPath: url.path) else { return }
+        let interactionController = UIDocumentInteractionController(url: url)
+        interactionController.delegate = self
+        interactionController.presentPreview(animated: true)
+    }
+
+    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
+        if let navigationController = self.navigationController {
+            return navigationController
+        }
+        return self
+    }
+
+    // MARK: open share menu
     func showShareMenu(messageModel: MessageViewModel) {
         let conversation = self.viewModel.conversation.value.conversationId
         let accountId = self.viewModel.conversation.value.accountId
         if let file = messageModel.transferedFile(conversationID: conversation, accountId: accountId) {
-            self.presentActivitycontrollerWithItems(items: [file])
+            self.presentActivityControllerWithItems(items: [file])
             return
         }
         if messageModel
             .getURLFromPhotoLibrary(conversationID: conversation,
                                     completionHandler: { [weak self, weak messageModel] url in
                                         if let url = url {
-                                            self?.presentActivitycontrollerWithItems(items: [url])
+                                            self?.presentActivityControllerWithItems(items: [url])
                                             return
                                         }
                                         guard let messageModel = messageModel else { return }
@@ -980,16 +1021,15 @@ class ConversationViewController: UIViewController,
         if let image = messageModel.getTransferedImage(maxSize: 250,
                                                        conversationID: conversationId,
                                                        accountId: accountId) {
-            self.presentActivitycontrollerWithItems(items: [image])
+            self.presentActivityControllerWithItems(items: [image])
         }
     }
 
-    func presentActivitycontrollerWithItems(items: [Any]) {
+    func presentActivityControllerWithItems(items: [Any]) {
         let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
         activityViewController.popoverPresentationController?.sourceView = self.view
         activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection()
         activityViewController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.maxX, width: 0, height: 0)
-        activityViewController.excludedActivityTypes = [UIActivity.ActivityType.airDrop]
         self.present(activityViewController, animated: true, completion: nil)
     }
 
@@ -1001,8 +1041,8 @@ class ConversationViewController: UIViewController,
         let screenSize = UIScreen.main.bounds
         let screenWidth = screenSize.width
         let screenHeight = screenSize.height
-        let newFrame = CGRect(x: 0, y: -statusBarHeight, width: screenWidth, height: screenHeight)
-        let initialFrame = CGRect(x: 0, y: screenHeight, width: screenWidth, height: screenHeight)
+        let newFrame = CGRect(x: 0, y: -statusBarHeight, width: screenWidth, height: screenHeight + statusBarHeight)
+        let initialFrame = CGRect(x: 0, y: screenHeight, width: screenWidth, height: screenHeight + statusBarHeight)
         contactPickerVC.view.frame = initialFrame
         self.view.addSubview(contactPickerVC.view)
         contactPickerVC.didMove(toParent: self)
@@ -1063,7 +1103,7 @@ extension ConversationViewController: UITableViewDataSource {
             self.transferCellSetup(item, cell, tableView, indexPath)
             self.locationCellSetup(item, cell)
             self.deleteCellSetup(cell)
-            self.shareMessageCellSeUp(cell, item: item)
+            self.messageCellActionsSetUp(cell, item: item)
             self.tapToShowTimeCellSetup(cell)
 
             return cell
@@ -1089,12 +1129,47 @@ extension ConversationViewController: UITableViewDataSource {
             .disposed(by: cell.disposeBag)
     }
 
-    private func shareMessageCellSeUp(_ cell: MessageCell, item: MessageViewModel) {
+    private func messageCellActionsSetUp(_ cell: MessageCell, item: MessageViewModel) {
         cell.shareMessage
+            .observeOn(MainScheduler.instance)
+            .subscribe(onNext: { [weak self, weak item] (shouldShare) in
+                guard shouldShare, let item = item else { return }
+                self?.showShareMenu(messageModel: item)
+            })
+            .disposed(by: cell.disposeBag)
+        cell.forwardMessage
+            .observeOn(MainScheduler.instance)
+            .subscribe(onNext: { [weak self, weak item] (shouldForward) in
+                guard shouldForward, let item = item else { return }
+                self?.viewModel.slectContactsToShareMessage(message: item)
+            })
+            .disposed(by: cell.disposeBag)
+        cell.saveMessage
+            .observeOn(MainScheduler.instance)
+            .subscribe(onNext: { [weak self, weak cell] (shouldSave) in
+                guard shouldSave, let cell = cell, let image = cell.transferedImage else { return }
+                self?.saveImageToGalery(image: image)
+            })
+            .disposed(by: cell.disposeBag)
+        cell.resendMessage
             .observeOn(MainScheduler.instance)
             .subscribe(onNext: { [weak self, weak item] (shouldResend) in
                 guard shouldResend, let item = item else { return }
-                self?.viewModel.slectContactsToShareMessage(message: item)
+                self?.viewModel.resendMessage(message: item)
+            })
+            .disposed(by: cell.disposeBag)
+        cell.previewMessage
+            .observeOn(MainScheduler.instance)
+            .subscribe(onNext: { [weak self, weak item, weak cell] (shouldPreview) in
+                guard shouldPreview, let item = item, let cell = cell, let self = self, let initialFrame = cell.getInitialFrame() else { return }
+                let player = item.getPlayer(conversationViewModel: self.viewModel)
+                let image = cell.transferedImage
+                if player == nil && image == nil {
+                    self.openDocument(messageModel: item)
+                    return
+                }
+                self.inputAccessoryView.isHidden = true
+                self.viewModel.openFullScreenPreview(parentView: self, viewModel: player, image: image, initialFrame: initialFrame, delegate: cell)
             })
             .disposed(by: cell.disposeBag)
     }
@@ -1196,13 +1271,16 @@ extension ConversationViewController: UITableViewDataSource {
                 .disposed(by: cell.disposeBag)
             cell.openPreview
                 .subscribe(onNext: { [weak self, weak item, weak cell] open in
-                    guard let self = self, open,
-                        let initialFrame = cell?.getInitialFrame() else { return }
-                    let player = item?.getPlayer(conversationViewModel: self.viewModel)
-                    let image = cell?.transferedImage
-                    if player == nil && image == nil { return }
+                    guard let self = self, open, let cell = cell, let item = item,
+                        let initialFrame = cell.getInitialFrame() else { return }
+                    let player = item.getPlayer(conversationViewModel: self.viewModel)
+                    let image = cell.transferedImage
+                    if player == nil && image == nil {
+                        self.openDocument(messageModel: item)
+                        return
+                    }
                     self.inputAccessoryView.isHidden = true
-                    self.viewModel.openFullScreenPreview(parentView: self, viewModel: player, image: image, initialFrame: initialFrame)
+                    self.viewModel.openFullScreenPreview(parentView: self, viewModel: player, image: image, initialFrame: initialFrame, delegate: cell)
                 })
                 .disposed(by: cell.disposeBag)
             cell.playerHeight
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
index 932969b54..20f8fd5e9 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
@@ -703,8 +703,8 @@ extension ConversationViewModel {
                                                         contactUri: self.conversation.value.participantUri)
     }
 
-    func openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect) {
-        self.stateSubject.onNext(ConversationState.openFullScreenPreview(parentView: parentView, viewModel: viewModel, image: image, initialFrame: initialFrame))
+    func openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect, delegate: PreviewViewControllerDelegate) {
+        self.stateSubject.onNext(ConversationState.openFullScreenPreview(parentView: parentView, viewModel: viewModel, image: image, initialFrame: initialFrame, delegate: delegate))
     }
 }
 
@@ -764,6 +764,36 @@ extension ConversationViewModel {
         self.changeConversationIfNeeded(items: selectedContacts)
     }
 
+    func resendMessage(message: MessageViewModel) {
+        guard !message.message.isGenerated,
+              !message.message.isLocationSharing else { return }
+        if !message.message.isTransfer {
+            self.sendMessage(withContent: message.content, contactURI: conversation.value.participantUri)
+            return
+        }
+        let conversationId = self.conversation.value.conversationId
+        let accountId = self.conversation.value.accountId
+        var fileName = message.content
+        if message.content.contains("\n") {
+            guard let substring = message.content.split(separator: "\n").first else { return }
+            fileName = String(substring)
+        }
+        if let url = message.transferedFile(conversationID: conversationId, accountId: accountId) {
+            self.sendFile(filePath: url.path, displayName: fileName, contactHash: self.conversation.value.hash)
+            return
+        }
+        if let image = message.getTransferedImage(maxSize: 200, conversationID: conversationId, accountId: accountId) {
+            let identifier = message.transferFileData.identifier
+            if identifier != nil {
+                self.sendImageFromPhotoLibraty(image: image, imageName: fileName, localIdentifier: identifier, contactHash: self.conversation.value.hash)
+                return
+            }
+            if let data = image.jpegData(compressionQuality: 100) {
+                self.sendAndSaveFile(displayName: fileName, imageData: data, contactHash: self.conversation.value.hash, conversation: self.conversation.value.conversationId)
+            }
+        }
+    }
+
     func slectContactsToShareMessage(message: MessageViewModel) {
         guard !message.message.isGenerated,
             !message.message.isLocationSharing else { return }
diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift
index c401b65f9..2ffb909e3 100644
--- a/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift
@@ -68,6 +68,7 @@ class MessageViewModel {
     var isComposingIndicator: Bool = false
 
     var isLocationSharingBubble: Bool { return self.message.isLocationSharing }
+    var isText: Bool { return !self.message.isLocationSharing && !self.message.isGenerated && !self.message.isTransfer }
 
     private let disposeBag = DisposeBag()
     let injectBug: InjectionBag
diff --git a/Ring/Ring/Features/Conversations/Preview/PreviewViewController.storyboard b/Ring/Ring/Features/Conversations/Preview/PreviewViewController.storyboard
index eebbc9187..221f9d69e 100644
--- a/Ring/Ring/Features/Conversations/Preview/PreviewViewController.storyboard
+++ b/Ring/Ring/Features/Conversations/Preview/PreviewViewController.storyboard
@@ -1,10 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="edr-AU-dxH">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="edr-AU-dxH">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
+        <capability name="Image references" minToolsVersion="12.0"/>
         <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>
     <scenes>
@@ -23,6 +25,41 @@
                             <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kNJ-67-zFf">
                                 <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                             </imageView>
+                            <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="3aB-Lr-pJQ">
+                                <rect key="frame" x="0.0" y="772" width="414" height="90"/>
+                                <subviews>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Gvs-6O-5Fs">
+                                        <rect key="frame" x="0.0" y="33" width="103.5" height="24"/>
+                                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <state key="normal">
+                                            <imageReference key="image" image="ic_share" symbolScale="large"/>
+                                        </state>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GFG-Jq-Pqx">
+                                        <rect key="frame" x="103.5" y="31.5" width="103.5" height="27"/>
+                                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <state key="normal">
+                                            <imageReference key="image" image="ic_forward" symbolScale="large"/>
+                                        </state>
+                                    </button>
+                                    <button opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5q1-cC-MI7">
+                                        <rect key="frame" x="207" y="31.5" width="103.5" height="27"/>
+                                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <state key="normal" image="ic_save">
+                                            <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/>
+                                        </state>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5pB-7e-B3j">
+                                        <rect key="frame" x="310.5" y="31.5" width="103.5" height="27"/>
+                                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <state key="normal" image="ic_conversation_remove"/>
+                                    </button>
+                                </subviews>
+                                <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.7987211045" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="90" id="BTB-tx-Psf"/>
+                                </constraints>
+                            </stackView>
                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="umc-Uj-AVs">
                                 <rect key="frame" x="0.0" y="0.0" width="414" height="80"/>
                                 <constraints>
@@ -32,21 +69,25 @@
                             <view contentMode="scaleToFill" id="Jga-0U-qXG" customClass="PlayerView" customModule="Ring" customModuleProvider="target">
                                 <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                                <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                                <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                             </view>
-                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s0z-cK-ISY">
-                                <rect key="frame" x="340" y="44" width="49" height="35"/>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="s0z-cK-ISY">
+                                <rect key="frame" x="341" y="44" width="48" height="35"/>
                                 <fontDescription key="fontDescription" type="system" pointSize="19"/>
                                 <state key="normal" title="Close">
                                     <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 </state>
                             </button>
                         </subviews>
+                        <viewLayoutGuide key="safeArea" id="dJP-6X-zM4"/>
                         <constraints>
                             <constraint firstItem="s0z-cK-ISY" firstAttribute="top" secondItem="dJP-6X-zM4" secondAttribute="top" priority="250" id="0CU-8Q-b0S"/>
+                            <constraint firstItem="3aB-Lr-pJQ" firstAttribute="leading" secondItem="dJP-6X-zM4" secondAttribute="leading" id="26g-Gx-ObU"/>
                             <constraint firstItem="9Qb-6X-Utl" firstAttribute="leading" secondItem="QAC-jd-TeH" secondAttribute="leading" id="39d-qj-71g"/>
                             <constraint firstItem="s0z-cK-ISY" firstAttribute="trailing" secondItem="Jga-0U-qXG" secondAttribute="trailing" constant="-25" id="IcL-2k-lX8"/>
+                            <constraint firstItem="dJP-6X-zM4" firstAttribute="trailing" secondItem="3aB-Lr-pJQ" secondAttribute="trailing" id="Mj0-Nc-ouz"/>
                             <constraint firstItem="Jga-0U-qXG" firstAttribute="top" secondItem="QAC-jd-TeH" secondAttribute="top" id="Ome-jS-TQa"/>
+                            <constraint firstItem="dJP-6X-zM4" firstAttribute="bottom" secondItem="3aB-Lr-pJQ" secondAttribute="bottom" id="QMX-S4-F1Q"/>
                             <constraint firstAttribute="bottom" secondItem="9Qb-6X-Utl" secondAttribute="bottom" id="R1V-fE-JGu"/>
                             <constraint firstItem="umc-Uj-AVs" firstAttribute="top" secondItem="QAC-jd-TeH" secondAttribute="top" id="RVi-sm-LCM"/>
                             <constraint firstItem="umc-Uj-AVs" firstAttribute="leading" secondItem="dJP-6X-zM4" secondAttribute="leading" id="XwX-7d-vQ6"/>
@@ -61,12 +102,14 @@
                             <constraint firstItem="Jga-0U-qXG" firstAttribute="leading" secondItem="dJP-6X-zM4" secondAttribute="leading" id="lQU-Ek-RAG"/>
                             <constraint firstItem="9Qb-6X-Utl" firstAttribute="top" secondItem="QAC-jd-TeH" secondAttribute="top" id="nIf-wb-wOx"/>
                         </constraints>
-                        <viewLayoutGuide key="safeArea" id="dJP-6X-zM4"/>
                     </view>
                     <nil key="simulatedTopBarMetrics"/>
                     <nil key="simulatedBottomBarMetrics"/>
                     <connections>
                         <outlet property="backgroundView" destination="9Qb-6X-Utl" id="nva-VK-fcG"/>
+                        <outlet property="buttonsContainer" destination="3aB-Lr-pJQ" id="u7z-BG-n68"/>
+                        <outlet property="deleteButton" destination="5pB-7e-B3j" id="Fcj-G9-OLd"/>
+                        <outlet property="forwardButton" destination="GFG-Jq-Pqx" id="Vot-bV-hkU"/>
                         <outlet property="gradientView" destination="umc-Uj-AVs" id="q4m-Ib-c0V"/>
                         <outlet property="hideButton" destination="s0z-cK-ISY" id="PeP-TI-Y2O"/>
                         <outlet property="imageBottomConstraint" destination="Z3A-GV-6Py" id="4dp-wF-Ix2"/>
@@ -75,6 +118,8 @@
                         <outlet property="imageTrailingConstraint" destination="Zxa-bY-zaY" id="gac-QR-lKz"/>
                         <outlet property="imageView" destination="kNJ-67-zFf" id="rTG-bA-Gz6"/>
                         <outlet property="playerView" destination="Jga-0U-qXG" id="nNg-vJ-wZp"/>
+                        <outlet property="saveButton" destination="5q1-cC-MI7" id="QG0-Vq-ETD"/>
+                        <outlet property="shareButton" destination="Gvs-6O-5Fs" id="FNx-Hm-Hav"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="5Ku-db-uak" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
@@ -82,4 +127,13 @@
             <point key="canvasLocation" x="137.68115942028987" y="96.428571428571431"/>
         </scene>
     </scenes>
+    <resources>
+        <image name="ic_conversation_remove" width="27" height="27"/>
+        <image name="ic_forward" width="27" height="27"/>
+        <image name="ic_save" width="27" height="27"/>
+        <image name="ic_share" width="24" height="24"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
 </document>
diff --git a/Ring/Ring/Features/Conversations/Preview/PreviewViewController.swift b/Ring/Ring/Features/Conversations/Preview/PreviewViewController.swift
index 0d55efeb2..a70802744 100644
--- a/Ring/Ring/Features/Conversations/Preview/PreviewViewController.swift
+++ b/Ring/Ring/Features/Conversations/Preview/PreviewViewController.swift
@@ -27,6 +27,13 @@ enum PrevewType {
     case image
 }
 
+protocol PreviewViewControllerDelegate: class {
+    func deleteFile()
+    func shareFile()
+    func forwardFile()
+    func saveFile()
+}
+
 class PreviewViewController: UIViewController, StoryboardBased, ViewModelBased {
 // MARK: - outlets
 @IBOutlet weak var playerView: PlayerView!
@@ -38,12 +45,18 @@ class PreviewViewController: UIViewController, StoryboardBased, ViewModelBased {
 @IBOutlet weak var imageBottomConstraint: NSLayoutConstraint!
 @IBOutlet weak var backgroundView: UIView!
 @IBOutlet weak var gradientView: UIView!
+@IBOutlet weak var shareButton: UIButton!
+@IBOutlet weak var deleteButton: UIButton!
+@IBOutlet weak var forwardButton: UIButton!
+@IBOutlet weak var saveButton: UIButton!
+@IBOutlet weak var buttonsContainer: UIStackView!
 
 // MARK: - members
     let disposeBag = DisposeBag()
     var viewModel: PreviewControllerModel!
     var tapGestureRecognizer: UITapGestureRecognizer!
     var type: PrevewType = .player
+    weak var delegate: PreviewViewControllerDelegate?
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -67,8 +80,41 @@ class PreviewViewController: UIViewController, StoryboardBased, ViewModelBased {
             .disposed(by: self.disposeBag)
         self.hideButton.centerYAnchor.constraint(equalTo: self.playerView.muteAudio.centerYAnchor, constant: 0).isActive = true
         self.hideButton.setTitle(L10n.Global.close, for: .normal)
+        self.shareButton.isUserInteractionEnabled = self.type == .image
+        self.deleteButton.isUserInteractionEnabled = self.type == .image
+        self.forwardButton.isUserInteractionEnabled = self.type == .image
+        buttonsContainer.isHidden = self.type != .image
         if self.type == .image, let image = self.viewModel.image {
             self.imageView.image = image
+            let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(startZooming(_:)))
+            imageView.isUserInteractionEnabled = true
+            imageView.addGestureRecognizer(pinchGesture)
+            self.shareButton.rx.tap
+                .subscribe(onNext: { [weak self] in
+                    self?.share()
+                })
+                .disposed(by: self.disposeBag)
+            self.deleteButton.rx.tap
+                .subscribe(onNext: { [weak self] in
+                    self?.parent?.inputAccessoryView?.isHidden = false
+                    self?.removeChildController()
+                    self?.delete()
+                })
+                .disposed(by: self.disposeBag)
+            self.forwardButton.rx.tap
+                .subscribe(onNext: { [weak self] in
+                    self?.forward()
+                    self?.parent?.inputAccessoryView?.isHidden = false
+                    self?.removeChildController()
+                })
+                .disposed(by: self.disposeBag)
+            self.saveButton.rx.tap
+                .subscribe(onNext: { [weak self] in
+                    self?.parent?.inputAccessoryView?.isHidden = false
+                    self?.removeChildController()
+                    self?.save()
+                })
+                .disposed(by: self.disposeBag)
             return
         }
         guard let model = self.viewModel.playerViewModel, let playerView = playerView else { return }
@@ -77,6 +123,14 @@ class PreviewViewController: UIViewController, StoryboardBased, ViewModelBased {
         self.view.addGestureRecognizer(tapGestureRecognizer)
     }
 
+    @objc
+     private func startZooming(_ sender: UIPinchGestureRecognizer) {
+       let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
+       guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
+       sender.view?.transform = scale
+       sender.scale = 1
+     }
+
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
         self.navigationController?.setNavigationBarHidden(true, animated: animated)
@@ -120,4 +174,25 @@ class PreviewViewController: UIViewController, StoryboardBased, ViewModelBased {
                         self.view.layoutIfNeeded()
             }, completion: nil)
     }
+    func share() {
+        if let delegate = self.delegate {
+            delegate.shareFile()
+        }
+    }
+    func delete() {
+        if let delegate = self.delegate {
+            delegate.deleteFile()
+        }
+    }
+    func forward() {
+        if let delegate = self.delegate {
+            delegate.forwardFile()
+        }
+    }
+
+    func save() {
+        if let delegate = self.delegate {
+            delegate.saveFile()
+        }
+    }
 }
diff --git a/Ring/Ring/Protocols/ConversationNavigation.swift b/Ring/Ring/Protocols/ConversationNavigation.swift
index 22c741bb2..f01bda3cc 100644
--- a/Ring/Ring/Protocols/ConversationNavigation.swift
+++ b/Ring/Ring/Protocols/ConversationNavigation.swift
@@ -36,7 +36,7 @@ enum ConversationState: State {
     case fromCallToConversation(conversation: ConversationViewModel)
     case needAccountMigration(accountId: String)
     case accountModeChanged
-    case openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect)
+    case openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect, delegate: PreviewViewControllerDelegate)
     case replaceCurrentWithConversationFor(participantUri: String)
 }
 
@@ -75,8 +75,8 @@ extension ConversationNavigation where Self: Coordinator, Self: StateableRespons
                     self.migrateAccount(accountId: accountId)
                 case .accountModeChanged:
                     self.accountModeChanged()
-                case .openFullScreenPreview(let parentView, let viewModel, let image, let initialFrame):
-                    self.openFullScreenPreview(parentView: parentView, viewModel: viewModel, image: image, initialFrame: initialFrame)
+                case .openFullScreenPreview(let parentView, let viewModel, let image, let initialFrame, let delegate):
+                    self.openFullScreenPreview(parentView: parentView, viewModel: viewModel, image: image, initialFrame: initialFrame, delegate: delegate)
                 default:
                     break
                 }
@@ -105,9 +105,10 @@ extension ConversationNavigation where Self: Coordinator, Self: StateableRespons
                      withStateable: recordFileViewController.viewModel)
     }
 
-    func openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect) {
+    func openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect, delegate: PreviewViewControllerDelegate) {
         if viewModel == nil && image == nil { return }
         let previewController = PreviewViewController.instantiate(with: self.injectionBag)
+        previewController.delegate = delegate
         if let viewModel = viewModel {
             previewController.viewModel.playerViewModel = viewModel
             previewController.type = .player
diff --git a/Ring/Ring/Resources/Images.xcassets/Contents.json b/Ring/Ring/Resources/Images.xcassets/Contents.json
index 37c3608ab..18a5a36fd 100644
--- a/Ring/Ring/Resources/Images.xcassets/Contents.json
+++ b/Ring/Ring/Resources/Images.xcassets/Contents.json
@@ -1,9 +1,9 @@
 {
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
     "compression-type" : "lossy"
   }
-}
\ No newline at end of file
+}
diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/Contents.json
index e73088e1e..07738c2f4 100644
--- a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/Contents.json
+++ b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/Contents.json
@@ -1,23 +1,27 @@
 {
   "images" : [
     {
+      "filename" : "trash-can-outline.png",
       "idiom" : "universal",
-      "filename" : "delete-2.png",
       "scale" : "1x"
     },
     {
+      "filename" : "trash-can-outline-2.png",
       "idiom" : "universal",
-      "filename" : "delete.png",
       "scale" : "2x"
     },
     {
+      "filename" : "trash-can-outline-3.png",
       "idiom" : "universal",
-      "filename" : "delete-1.png",
       "scale" : "3x"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
\ No newline at end of file
+}
diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-1.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-1.png
deleted file mode 100644
index 04d7a8edcbfb56087078e6509c0e8f39b3ac2461..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 391
zcmeAS@N?(olHy`uVBq!ia0y~yVDJWE4mJh`1`EHcR}2gcY)RhkE)4%caKYZ?lNlHo
zZ9H8ZLn`LHy=~}s*g(eZWB5Cko-->7WE@13k8X5GTJlJ3DR=6!*ubL?&U?O5xaa>X
ze`}JxX{eEiisz&*U90&2Tss)faoPRm&1tyqS#A>i{lLPccU3vtma}eHtaq*NU%AVZ
ziN6kio5!DES~uywuC&aGqcO|b^?xSXmd0&N>(jf;YP!)(H2c|$wO3oi4ov&EJ?Lys
zWBbR+9+Up9S-76JJ+SOz*w>la^EUs}O=j&8H3&Np!AMPpgw*ToDY08VTc&)x5b>V*
z`u1z3`?r7j|3AU#bf(di>P7V}lOp{^6-t@+FjW6?|7ltMV5VfvVinIxDqG7~ZpIt?
VTkC24WME)m@O1TaS?83{1ORG*pIiU{

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-2.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete-2.png
deleted file mode 100644
index 8e741fc4248e814b22b74fb58cc2891b3e4ea0da..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 272
zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}Y)RhkE)4%caKYZ?lNlHo
zj(WN{hD5Z!y=Iu@6e!U8@caS~F2_xcItMSAEp<J>yQ6=NqW1=W!8b>ag{=wN#bTZ?
z*~vw5<)xP2^L|NeDB!!3rk8&1{N7-mmC~!P9{AkBZ}We3zTrVTrU$I~8RGA%B*K2|
z`w+WA{$t<Or3t}*SmtMHu6V^d&%o_@&?953q`ugI{T5ewA96G=n8Q2m`YfgA3x53n
zP@o&SMM!}WiWcxa5SbBtb2VRrEJwgj-s#ug+1D=W4=<5slizS)%|iQ9_Ft^aq;1@8
U)^L_FFfcH9y85}Sb4q9e0GK3g%>V!Z

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/delete.png
deleted file mode 100644
index d182d87146a01e56fc1aff7eb7ef4676852001e8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 306
zcmeAS@N?(olHy`uVBq!ia0y~yU@!t<4mJh`208nVjSLJ7Y)RhkE)4%caKYZ?lNlHo
zo_o4DhGaCpy}6g~V1h{dNA~8{*&A+lTrHJot$xsz!^W0(I43jb5!bAUCDWvC&)fI6
z;Kcvwdu|6lzqrJ?vBP75t^l*=vCc-{yYaUS&z)_USXjpXg*$EDflC#8r3(ztJ&T(B
z_w}~YFjwy~Ud7_WK9{p@CUUvdyw7tvu%u65BEz-g`Um!H)cvHZ<DBX=>457hqqiBA
zfBvzwRxB3C7kSj{v?s8`oa>{T;ysNc(g;CK;ZGSk-8tp26J}0uKA;pTJA20q0gKD`
z<ZaXw5@Oe#@qW`^SXIS1e`^Kf^)DSAe^#2W`B%7h@5)W`3=9kmp00i_>zopr06cJp
A;{X5v

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-2.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a3e09cebd4b5665f22a9792bb403e40986e8d56
GIT binary patch
literal 562
zcmeAS@N?(olHy`uVBq!ia0y~yU@!w=4mJh`h91|fy9^8rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%z-Z#>;uw-~@9iu@zas_$uKN?US)8gF^&A`z
zitb?fe#5(feT5~X=uxh~;37sVmyL^VCe3zlW|3CRl}Tzh{BIhlq&_dbKQ={2GP&oc
zQtw~K2QimyK5q^6dYSp^;0@JnD)XP-D175<E3ov%)Gmf#_FbYSUe=HE%8u!X^+ujA
z3!i`8+f)7iy!{@#bVZ`~?yI|T%KiSb<F8iV4A~N4;wJujvsu=f(DM-{I%2%tu7{H@
zZa`tq5`F#qut|ICw%5t48*~3ni<r+8*Q*r2dF72Y5qVl=+zrJK76s(owJHnyXc+!x
zZE?NY%Y73freB|PZ0E{~aL+|~_Yd$?$P{Qj*lxq{+qIq7f+?@Tmvtd;0k6!nW0pS{
zxC?Y%hFo%7Xc*&N<)^t<{MSCiV}F$G80KD>apUM+!&(Lg2GtVRh?11Vl2ohYqSVBa
zR0bmhBST#S6I~;t5JOWdV@oSjb8Q0yD+7a*7p5AZXvob^$xN%ntzk#6ZVxE_JYD@<
J);T3K0RU*8%jW<9

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-3.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline-3.png
new file mode 100644
index 0000000000000000000000000000000000000000..4dc5af188f782b8018e9aa8d4da19daf5b200415
GIT binary patch
literal 769
zcmeAS@N?(olHy`uVBq!ia0y~yU<d?Z4mJh`hE@GuW(*7rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%z{Kq7;uunK>+PM5-cti*j(vRZFVnqjQifo;
z`thi!KkQM`t!)`=9Hi#Fw&!qa3cKDiO=gk;$1LePTv}3(9;w;!N;Q4C_raF``wW};
zp6^yaw|#!PcJKSW1^g~xFem)#>m!rqF_rf?=Y?KfESEj=ec`3#@J7|o6|rZ&T77gi
zdNotyrC7n{k9ST#+p6PTb;sgZUg%!q^RM5l<i1F{C2?7K`esKZDV0TF;^2yMok`Oy
zb_T{V#PR-lCH)}sy(<%c<JHFG^2Yq*_U}Wl-aC`7o>_QG_i)<sjOEXFSf=)MuYLTy
z$jrl~*~J7*ybvoqqy4OMukZ=uiwc6gLX;B=o^SVwXL`@x_jk^xAA1{<=U3}4i9WLG
z7Gv4DALhajD%V@`pM07#p@Xe6158}lBi*?qdrpn{yDgj_7JpZ=yp{6!(}9hxtA5=x
z+M)bN<^%T*liy}%)ecNu6?9tR?DXPLS*F{2vwObmGF6yiCO&BkQ_X|_zvQbq)`VPL
zeyvfbd)=!31C!4Mp3Ax`c){-yv*+)<-&23K*Lr@R`+D1ZQMH$B8`$o!t-Ji)xzC=j
zVf}&V2d<&DFKrigfWgflyv$1WLcxuT-5D4dR7+eVN>UO_QmvAUQWHy38H@~!40R1m
zbd8Kc3{9;}46F<+wG9lc3=CKl?>V4o$jwj5OsmALp|mFL4=6c$y85}Sb4q9e0K$JR
AY5)KL

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline.png b/Ring/Ring/Resources/Images.xcassets/ic_conversation_remove.imageset/trash-can-outline.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a02c464c273daa50ac11ff3c51d51ca0d56ccf2
GIT binary patch
literal 481
zcmeAS@N?(olHy`uVBq!ia0y~yV2}o34mJh`hTbb*LKzqsSkfJR9T^xl_H+M9WMyDr
zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7}%1$-CY>K
z;HUHMdIkmt&H|6fVg?3=1Tg-P?R1`jf#I>Ii(`n#@wZcL`5YZZTK8*nO?7Ee)yQc)
z5@sTl9kB9^V-mNSVvgfNg^Q=UI|N#vWVku1ov~RTa3)i&xc=PE=VszUTn(*aUb7fV
z{k4oY2XeUIbiT!EHSd;Uz~>|Vll<oWICW;)eVLw6o@4QI8KV<;w=_zBsN5A^(%AL-
zK3l2BtKBcl1f4u??cBFdKfX)xx9Gv7_rcE#|Ia@+DMH9QX=h;Hinlv|HUB=}$XcKu
zbLPyq{Qt%UdWUXa-COE;nPcB`)%`5x9$8+eisa7v1RdTzm%m9jfgwbB&fJEXGjo%!
z%@$~7|9AI6(e-9F83qOh)e_f;l9a@fRIB8o)Wnih1|tI_LtO(CT_d9qLsKhbODhvg
nZ36=<1A|c4cb`!-<mRVjrd8tBpdu0G4+?HiS3j3^P6<r_mguPQ

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/Contents.json
new file mode 100644
index 000000000..ac290e18f
--- /dev/null
+++ b/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "share-outline.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "share-outline-2.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "share-outline-4.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-2.png b/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..de5588ac43f6f007ac0ed86875cf637bfe253a30
GIT binary patch
literal 834
zcmeAS@N?(olHy`uVBq!ia0y~yU@!w=4mJh`h91|fy9^8rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%z~tiT;uw-~@9oUJ*&>b-?e)S>uH1<DF_EJ&
zB12iIg_&Eh$<=51LA5BBKm2QQ_oykXi0DvQWG1Au#e>7;G5a#UC;T2wU71@1OudRe
zy2y1$8^~<Gx%cMHyM||&Pm7j+-uHRW<}-UYYqK&nIw%Nmu>4DJa7+l7+qm1SOnQn5
z>zjLdNs~-eGB0hN!=JSC#80JXG3V3`mly5pw4D4o@J#2N^+_|I`b;!t?OF18(yB>p
zD!krSvkcy}E)h&V<2JF_S2>u`t>O5A^fwjr!rPOSudv@?+gGCT@qoAG0`?vJe>TO;
z)m#5$5oavpzU3l6n01&ZA1HlbR8SPW=F`)LxCf<NGb<lR6-d4C3taK(NrTphOzxSX
z57Y{dC~ez%r>`gI@T7U3w=O?>-L}Mf(y?B{`$cQlc{dn4*e;vJePCnH$L{^2jf@Y{
zeJ-gVIQOEwz2WnL&<Bbi#3ESinBp7VUOzBe`i=2@@}zsrdW^ycxXPw%&b)AE-tCXp
zyEX-NU769`dHce~kXLFFH?%W7FV%I=TH{%z{<3+=t|jsp(?c(<Y}xqJ?BId5Q?9OM
zE_UdBcJAh8*E6$TPkj}#YWk10va{Y^)PA5OJ7>Po5w4edc}G<88Oj^$PVd-iv{Y-0
zeU%OCB)(3wq@yM~5427@viiV^#W&U_ZEp2&J-OUmbsjsPbx*8z&C*3j*nfUsZtZ<2
zP`P<V@yaF3L(;F>FrM+SO7Dp*kGAr><SqJl16u{l9!4$8qvzk61_eE`Jh1HY2k{3C
z1&iKPvVi0ChT9)jZCCAr-&-H-VPIfTEpd$~Nl7e8wMs5ZO)N=eFfuSQ)HN{CH8Kh@
vG_^9eurf5#HZZU<F!+#gKNv+rZhlH;S|x4`jkf9ULFw7k)z4*}Q$iB}tTtF0

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-4.png b/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..618f70b96585daa2a263596b9d262b1a9e049bfc
GIT binary patch
literal 1188
zcmeAS@N?(olHy`uVBq!ia0y~yU<d?Z4mJh`hE@GuW(*7rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%z`V@U#WAGf*4x>>8M2`=?&o_KZWDdk&Zx<;
zYjRUYr?lvwh!xxyUwQ2+RPADm*}3X2$0K(^*YMbgkTtF%F+W+AYy!lSE^w_ASrx*{
zeAHpVF|%tj;>O*IUsR1}&MZ$?`+gw$>E50DtDn{1|MUI~w~A1wi_%071a(BrVQ$x4
z#s$KPSs%KpLf;6~POo72p|tJ9KJOO34w)vG4xJ(mA;m=}6g^u!l-kt<l{#b=xy;yb
zVxd<HXP3+-ml+dIEc9;S?6|`vv@msIw5!q<jUuaWXI3qW<G#ajhb1Qd<74)Q^H-~#
zIJ$jPxi&7%Y1t*e!@T}3+nc-x8}2jrcD<2#wdU8XU$ftSoV&~XTg+^+?cTLfe>W<6
zzR#DO_^RjEnq4;vmc}mI7yYPq`I`g1ch8=>J@K^l^$j+mpADC>M^Aj!{7RqK%6fI(
z(tU~FrWV8}u1V|+DPJvrWkPA--0=RD#joa0i|A?=TzPNRzg?V#-IcAjOWv(CtlRIp
zs(<zWFoEr>>Q>2xY&V^;xL)b%0=sy}d-bn4zqW}P=Z62+nE3SVjeh~luWZ-ikXgA|
zH~p2r>NC-bHB0uWZhm_2mDm-r-^aG)Iqc!F34Bz0pP}7}BmLUUKV}cC9!MpyZxFu0
zn!~<M;`>g4Ll+MjE^C~8;IQ<Qm&fKs)ow~*l?&I`_q&pF-e&s$+$Hl)?pN2|{A|bH
zh(nbRZeN`$)Hwg7dU^Y=Ns){9?2(Lo?fGW$f!bG2od@DqzrXn>u2T1}&<yWCD^fK>
zXU;g_wyxvV;sYOVsBcoTXDDwM__wNh!7ZVys~hK^SU00~;l<Sj!n)_Uw<v$`e^Bx-
zieXv<_W{cX`3bBRPT$rn<?7vTxK%4~ujG$uRsOuw8!I=>E0p^oX(8(rp=bDyW5?=4
z5o^CRmTG@=HB<jk{=oTx@q^O>hkmN5o3`bdTynh<tre&HKk&ZEYhLNfd%4#pOkZf7
zduA5rl-$g7tK6^56f>GK@kV6h@3ZfIu01}fDq`=ZqcYN-Z<Zaf`SbcUOMK}4!0)~a
z$5*UpDz~aTy={S=!9ktJD)Tm-?svTEevIXR%BA$Wo+IzV5)^+NpB}OAyyV09BYKJ(
z#hJdZ5a(K`zpCH$Oz@N&Z&hPBVq;D{zxrGGx#ZVnOxz3opDhf((ky$z_?0i~v&Q9T
zq@IU<4R~Wyxh%XoxUurV%{^TF$Ik_wxUzn}_iF!@>PgRED21MT!ebzAU9f)RvHni9
zTw?lD-saKQd+YX2TE)P?pjzS@QIe8al4_M)l$uzQ%3x$*WT<OkqHAOnVrXh*Y++?&
oqHSPcWnds{|F#Q7LvDUbW?Cg~4LM1npkjx?)78&qol`;+0CjH>L;wH)

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline.png b/Ring/Ring/Resources/Images.xcassets/ic_forward.imageset/share-outline.png
new file mode 100644
index 0000000000000000000000000000000000000000..7850257c550347ae850dcdd76efd3fc1daac3362
GIT binary patch
literal 547
zcmeAS@N?(olHy`uVBq!ia0y~yV2}o34mJh`hTbb*LKzqsSkfJR9T^xl_H+M9WMyDr
zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7}%1$-CY>K
z;HUHMdIkmt&H|6fVg?3=1Tg-P?R1`jfl<}d#WBR=_}i%qy;&S(-1dvU7YMZ27{SBV
zt#FF<pxMnNgN~;=GK_DSN^glOVg1iy8tJm8{O5sXH+-U81m+zSY}<P~wd|PgruaFv
z``^EPb8b`6T&qyl494Vrd(3A{4)sh=Z85Yq{-d%s=EaFs;%O=M`YdUUd<9%Q66Y$n
z2t;ghmG3#{xkVs@q5Z(};BS0)7}yV}ZOFJ;plHB)$C6e5fS$qJ!|D$AAMSql^&`{8
zB}<|`Uzfdfy0mWztEcpVIZHa@?K~$bdmf*3t4woh$;;iVuk1Xm{y?fi**>vf;D(mQ
zU#5ADabGgGus<>k{=o2tQQP5iqVI+iVkL7PYA5V$Fg>UgU66nOY#)1elFR1<+84e(
z@jb)7hj-1?2i||rZ#r!3*j)NAcOT=?2+3Z?_}mT#1_sp<*NBpo#FA92<f7EXl2isG
z10zFS0~1{%qYy(=D`Rsj6H9Fa11kdq@1A=OC>nC}Q!>*kaci);nDz%0VV<sjF6*2U
FngCJr$7%oo

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/Contents.json
new file mode 100644
index 000000000..35c0d5881
--- /dev/null
+++ b/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "filename" : "tray-arrow-down.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "tray-arrow-down-2.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "tray-arrow-down-4.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
diff --git a/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-2.png b/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..91ede05ff54b0705ab75972dde0002807dd11942
GIT binary patch
literal 760
zcmeAS@N?(olHy`uVBq!ia0y~yU@!w=4mJh`h91|fy9^8rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%!1&A4#W5t~-rHIB-qC>)$L71gdh{fvyLoZ+
zbH1{I55g`Tq3nV|Q<W|qy&JgSX}eOArmOhTgE##bEKJee!QJFC`A5lzWlNs_JGs|H
zeYyFb{hE8eKihL&UtRoOweSH0p7|HbnLUjerp^6icrt$B)BmA4afQ!nzsqv#<|V16
zB|SZI<WyVdR9<0iX|-vsOME@^**2V;vc!L({gk{JcQcamJ!bh?%*)=@zhvFw$G`t~
zPr3BuQ-@_xET5C--e4p9>iyIH9#PplEyO?ab*;uuUC;BJjsM=nOp7?>ry2FRSYV~x
zPG8TtCOb_wdc0C|%~D?x(tqbT&(5hWRi@5a)?50u!@Vc&J|FqoV^!-nkIB&!uY?s^
zO}bXwe!F<LSj~TS&$UcJxt`gq)jQtoe9lv|MCm8*0j-9zzRT7O*F5WFOZfCRE1i5_
zQR#8(mGV}%PZ!+T@|_J%$}?y&$O-dpX9)3>`}yj9=`Qt3wgf?k&CCC=By>LMJb&+A
zezeED$EnX}O$?d(WaayP>ubzAp6IEB?poh|u~;MNu*j>V@(ZcSDMu%*+qf)xLW!hR
zhJ4b@$wyvsZf(7A@^`^ov84Tu-csxZ%n4>Cx8|5I>{0!B{fF4@V~d2f81kIe&mGil
za1G}=@#A{K5>EzchGz^uEoKE33<azYxKHlBkjQh`fakD*+m!o^FMpi7cst60fq_A_
z#5JNMC9x#cD!C{%u_Tqj$iT=@*T6*A$SB0n)XK!r%GgBPz`)ADfMxgPSQHJp`6-!c
WmAEyOKFfXxN`;=TelF{r5}E)%WHVp@

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-4.png b/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..2564dcfee939661f5ab4d6b58bd2473999dc1902
GIT binary patch
literal 1092
zcmeAS@N?(olHy`uVBq!ia0y~yU<d?Z4mJh`hE@GuW(*7rEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%!0hJf;uunK>+PJA{k(x9?faWsX0QrMAKagy
zoOrQJthb{>Kv3`>bA7|o#Poxcrtw*+xNs;sF7MRraCzgiS9sd9m+2RmCRdp5E&o0D
z)w!S3-d|a3^}Krdz1=&v2MMq^3a~hCHh9kxrI=}Iu-5g(=9zatoy(6=c)2v{^Cj!;
zVLqSy5|jjlgvme`OgkrC^PF{OX}0gAEeoT!ma=+QZ|+^j=ozdfbN`#$RF%mkCf0TP
z%ipeKUA5{&nQEGB#hdwt?0aOFh(&m&SPL099xpy7`=xARRKm&=yM(-1{ym$peey1*
z<=$1Rihf;vbbeW%i|1t3uc14x1$%DZbN7nq&VWmkxsLw5J~31^jp?*iz@&MeQE%QZ
ziJkaa#dd}ErmiDWvOgj&G3ATT?V6+gGER14s!%pZjY{Ue?afn^-iL`ssu&7aEIPDP
z&QGGTzGIH^OTX4fzg2mkA8FfMAz7|+_T#$BH7@5Slq_y)@!O%BxvhNdU4hEl9u~fn
ziBgk)FIlrh^}w|`AKv=Si<oM{yo2M5%9^KY7qZjjtxjZ{ZqQP%Id!J(^}G$sA4uQ7
zo_SPi=7f0fD~-*Q($r35#ve4T`gCmPTJ>2s-4{%s*!QMo^?@kk>i41DuNSSES<7g~
zD1OW4sAxj-%-T<D4%epMKC_Q$cH?K$_Bb}PSvS+)OWjdDwBm&Bft_8>dKU#6F9>vg
z2zQ^SWBEl#nQ4E;^FOnXd9WC4mOsF^!2bCJ;{#^Kzn{&0J>SGjsi*4>=bRtvCJxN3
zPRt-PVjbeYUCd0_J+t=vxwWE8t@Gn%+~oJ%-Qc+3{F%C{=TqF1Rhd#A^;JF(bW_fM
z{?X0D#{rHm?k~Q$WLx;-Qu*yOL|He+$h&IaKHd2IUZ`nDYTCC|p$k5^N==GmIK5|c
zKEwL6Ihk=A-k5*vDF4R$upn^58sX(en;5rT+Rf$uj>*>5JHK`A4Bi-ap33k?M|2Zv
zZ{A9MDRTVGJ;v;Yy9a6=?oI7AJoV}h^BTj=+f$jd_Ak`jeN_D7hojwQKl%1#URqTN
zN!l<{xI5lR_vztH7i1zB7#LJbTq8<S5=&C8l8aIkOHvt(42%qQ4NP>6j6w`etxOE9
qOboOQ46F<cei{A?N70a*pOTqYiCaT=j@xTcj`no*b6Mw<&;$Sv@V`O;

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down.png b/Ring/Ring/Resources/Images.xcassets/ic_save.imageset/tray-arrow-down.png
new file mode 100644
index 0000000000000000000000000000000000000000..16f6e969a6fcf77feedeeca48a62a6a1fb52d94b
GIT binary patch
literal 546
zcmeAS@N?(olHy`uVBq!ia0y~yV2}o34mJh`hTbb*LKzqsSkfJR9T^xl_H+M9WMyDr
zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7}%1$-CY>K
z;HUHMdIkmt&H|6fVg?3=1Tg-P?R1`jfl<ZN#WBR=_}eMAUd(|at>;Cxt))91V;ZtM
zgp+jcIB^xSW{J97N>OLrK2a$lH(5bQ$5miTg5u0?n?LZ`wY5C4Y<8V-dC$z#w%@Ct
zpY0Dh$y(B=c%k=e-v7pOm6suxvey*8w)bD!l&iqAgVitW%a(??g>xCE|H+ug*w}cT
zjWOfh*4IiKqzpJ4-U_hEFvr*Z=aCWd4QF4*Sn%fhV}*+%D~|KLWiWS0c0II5%^|mO
z`NPjv-3iL(->2uK>3d9l$$2CG(*5uA0>h$zUp3!c?&<5vA6GTyipl2frRgrU>1p3I
zU;12{yJXwU+9N*}|NoU{Hn~Lbb+=zwNaCiM>%$LJa&9g!F0N-)aq!yd)u@sj!B?T7
zIdMy{)25J1duQ&Q@<T-{chf#bD~r5I&a1&uqFUk_QIe8al4_M)l$uzQ%3x$*WT<Ok
zqHAOnVrXh*VrXS#scm3jWni%K*d}HM1_p$N-29Zxv`X9>Zp`M|4~j2OS3j3^P6<r_
D@gl>t

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/Contents.json b/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/Contents.json
new file mode 100644
index 000000000..c9e7ccc34
--- /dev/null
+++ b/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "export-variant-3.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "export-variant-4.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "export-variant.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-3.png b/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-3.png
new file mode 100644
index 0000000000000000000000000000000000000000..16dd4d7800bb414d63df05af1e6dffd5c585273a
GIT binary patch
literal 386
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoSkfJR9T^xl_H+M9WMyDr
zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7}%1$-CY>K
z;HUHMdIkmt&H|6fVg?3=1Tg-P?R1`jfuY6I#WBR=_}eK5c^ec2Sl;)f-k7kF|J8@g
zxk>Vm*@c^>91l)7QP(J2)~^sWsX=f?(X4zg?vH0KJL#R-tjehH%xCf!HbsU6hH16@
z<(+eMF0@}s_`7JmmetIYe^xzL&%S?t0aMLZ!`Y#s-QEF=_6&1uFT1+zx>0}sWSfcs
z-=`yYp5#4QAZk-^N}g>lPc;JrgKCLuL`h0wNvc(HQEFmIDua=Mk)f`EiLQ}Rh@q*K
sv5A$Dp|*j6m4QL1wbEh~4Y~O#nQ4`{HK?S`k_Ux{r>mdKI;Vst04%6^cmMzZ

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-4.png b/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..74cc4fafcf3c29ab6cc58f20e771aae97309c9bf
GIT binary patch
literal 557
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<YIEa{HEjtmSN`?>!lvNA9*
zC?tCX`7$t6sWC7#v@kIIVqjosc)`F>YQVtoDuIE)Y6b&?c)^@qfi?^b3~Wi>?k)^q
z@Y8vBJp%&+XMsm#F#`ib0vLbDb~?|%z^L!(;uw-~@9hlRyh9EGt@jsf;q<E7zR>sN
zM~%xaca*y(S-Do7bMWOBs%~_5j7i`;|0~^gw?TY*=HB0S=c`imAKWoe30JwlwwQ-A
zK6sjoa!sUA_@U{F3tP@dD;d?y6OujrT=k;ypA4Z%0X^c}ohp&p)4US2Vh+sGV4Ng+
zQh(CJTeokNKAa|QJ>ieyPKl!*?El1Nt4AH+V=`r*bX7t$g1JKSX=ce|$ph_X9=W+H
z%nk0Pi`-^kxs~x`8@G~?yw-))^ZiTj*On}tQ^=Sdb3jUb!?KFT_7LIaznHUwYFNP{
z$5_|EB-Nf}n=n+Y(*HH5neV|0-j0<A?zc3~{*%R=<o7I&F{i+8^_)qz4d#M3xAF#Q
zD=0-Xcr)C$$X|Yig<-zo!?`jRjE5Co_pP6PVlD#%gKCLuL`h0wNvc(HQEFmIDua=M
zk)f`EiLQ}Rh@q*Kv5A$DnYMv}m4U&8IWoE^8glbfGSez?Yj}8Y$wW}pdAj<!taD0e
F0syf$!G8b%

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant.png b/Ring/Ring/Resources/Images.xcassets/ic_share.imageset/export-variant.png
new file mode 100644
index 0000000000000000000000000000000000000000..44ab9a3edc40397e1ac9a818bdb47bb6b1157a99
GIT binary patch
literal 713
zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D4dSkfJR9T^xl_H+M9WMyDr
zP)PO&@?~JCQe$9fXklRZ#lXPO@PdJ%)PRBERRRNp)eHs(@q#(K0&N%=7}%1$-CY>K
z;HUHMdIkmt&H|6fVg?3=1Tg-P?R1`jf$_Abi(^Q|t+%%g{SG^bv^<P#@nBXzAv$3}
zvvUD+)`BMEfFiak3-1*jEe{kPu+3W7F;Qts#-8(K*E*lSpT6hshNDbo59UaFtGxHL
zn=&tX!c~iU$;_Sg7H&~_ynPd;9`s1BR6J)f(WvlPtH=>a_8{*$cimobq?=E)I(Acc
z$-DxywkclB*E(V>W4w+&zpZV1;DYgl6AntuojfXB%oFEK*rRNz9BKM<=AEk-c>5Vb
zZEx$IJn0td>F0T_m|u6NbROrHJK85RQ`X;n-t%ksN2UiVzYkxVlU;P)@!;x0z6Ul-
zzs%jNQPh3crGV{%X2a?`u7V3rU$V{({Pt#I^c(I2i7C4dZI@sO(OkNCU!&Fky_qNC
zFF#k9*yzG0B<YcYo$-4qJ44$?{b!{=xo|7O)IZVwR(Qlmf%NJAm%iUff7f(E^h;dT
zblqPkVg(qhwDz7BOFb`FE1KALbn@%lS7Z)Edc9On5Bw$?vj25KVEuz_ChP?hyP3CC
ztepOYu_0SW?!CR+hnK(QRx|8jtYG^faj@{**L(UUvXfu_TV}7#z`&qd;u=wsl30>z
zm0XmXSdz+MWME{dYha>lWE5g(YGrI}Wo)KxU|?lnu<sT7DHIL4`6-!cmAEyW-@d;L
Pl&m~m{an^LB{Ts58vqYU

literal 0
HcmV?d00001

diff --git a/Ring/Ring/Resources/en.lproj/Localizable.strings b/Ring/Ring/Resources/en.lproj/Localizable.strings
index c9d80a5e2..6eaee9da8 100644
--- a/Ring/Ring/Resources/en.lproj/Localizable.strings
+++ b/Ring/Ring/Resources/en.lproj/Localizable.strings
@@ -26,6 +26,10 @@
 "global.versionName" = "Together";
 "global.close" = "Close";
 "global.share" = "Share";
+"global.forward" = "Forward";
+"global.save" = "Save";
+"global.resend" = "Resend";
+"global.preview" = "Preview";
 
 // Scan
 "scan.badQrCode" = "Bad QR code";
@@ -50,6 +54,7 @@
 "conversation.messagePlaceholder" = "Write message to ";
 "conversation.explanationSendingLocationTo" = "You are currently sharing your location with ";
 "conversation.explanationReceivingLocationFrom" = "You are currently receiving a live location from ";
+"conversation.errorSavingImage" = "Failed to save image to galery";
 
 //Invitations
 "invitations.noInvitations" = "No invitations";
-- 
GitLab