diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift
new file mode 100644
index 000000000..a76537140
--- /dev/null
+++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift
@@ -0,0 +1,172 @@
+/*
+ Copyright 2019 New Vector Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import UIKit
+import Reusable
+
+final class ContextualMenuItemView: UIView, NibOwnerLoadable {
+
+ // MARK: - Constants
+
+ private enum ColorAlpha {
+ static let normal: CGFloat = 1.0
+ static let highlighted: CGFloat = 0.3
+ }
+
+ private enum ViewAlpha {
+ static let normal: CGFloat = 1.0
+ static let disabled: CGFloat = 0.5
+ }
+
+ // MARK: - Properties
+
+ // MARK: Outlets
+
+ @IBOutlet private weak var imageView: UIImageView!
+ @IBOutlet private weak var titleLabel: UILabel!
+
+ // MARK: Private
+
+ private var originalImage: UIImage?
+
+ private var isHighlighted: Bool = false {
+ didSet {
+ self.updateView()
+ }
+ }
+
+ // MARK: Public
+
+ var titleColor: UIColor = .black {
+ didSet {
+ self.updateView()
+ }
+ }
+
+ var imageColor: UIColor = .black {
+ didSet {
+ self.updateView()
+ }
+ }
+
+ var isEnabled: Bool = true {
+ didSet {
+ self.updateView()
+ }
+ }
+
+ var action: (() -> Void)?
+
+ // MARK: Setup
+
+ private func commonInit() {
+ self.setupGestureRecognizer()
+ }
+
+ convenience init() {
+ self.init(frame: CGRect.zero)
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ self.loadNibContent()
+ self.commonInit()
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ self.loadNibContent()
+ self.commonInit()
+ }
+
+ // MARK: - Public
+
+ func fill(title: String, image: UIImage?) {
+ self.originalImage = image?.withRenderingMode(.alwaysTemplate)
+ self.titleLabel.text = title
+ self.updateView()
+ }
+
+ func fill(menuItem: RoomContextualMenuItem) {
+ self.fill(title: menuItem.title, image: menuItem.image)
+ self.action = menuItem.action
+ self.isEnabled = menuItem.isEnabled
+ }
+
+ // MARK: - Private
+
+ private func setupGestureRecognizer() {
+ let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
+ gestureRecognizer.minimumPressDuration = 0
+ self.addGestureRecognizer(gestureRecognizer)
+ }
+
+ private func updateView() {
+
+ let viewAlpha = self.isEnabled ? ViewAlpha.normal : ViewAlpha.disabled
+ let colorAlpha = self.isHighlighted ? ColorAlpha.highlighted : ColorAlpha.normal
+
+ self.updateTitleAndImageAlpha(viewAlpha)
+ self.imageView.tintColor = self.imageColor
+ self.updateTitleAndImageColorAlpha(colorAlpha)
+ }
+
+ private func updateTitleAndImageAlpha(_ alpha: CGFloat) {
+ self.imageView.alpha = alpha
+ self.titleLabel.alpha = alpha
+ }
+
+ private func updateTitleAndImageColorAlpha(_ alpha: CGFloat) {
+ let titleColor: UIColor
+ let image: UIImage?
+
+ if alpha < 1.0 {
+ titleColor = self.titleColor.withAlphaComponent(alpha)
+ image = self.originalImage?.vc_tintedImage(usingColor: self.imageColor.withAlphaComponent(alpha))
+ } else {
+ titleColor = self.titleColor
+ image = self.originalImage
+ }
+
+ self.titleLabel.textColor = titleColor
+ self.imageView.image = image
+ }
+
+ // MARK: - Actions
+
+ @objc private func buttonAction(_ sender: UILongPressGestureRecognizer) {
+ guard self.isEnabled else {
+ return
+ }
+
+ let isBackgroundViewTouched = sender.vc_isTouchingInside()
+
+ switch sender.state {
+ case .began, .changed:
+ self.isHighlighted = isBackgroundViewTouched
+ case .ended:
+ self.isHighlighted = false
+
+ if isBackgroundViewTouched {
+ self.action?()
+ }
+ case .cancelled:
+ self.isHighlighted = false
+ default:
+ break
+ }
+ }
+}
diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib
new file mode 100644
index 000000000..9c2ce9ac5
--- /dev/null
+++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift
new file mode 100644
index 000000000..ba32691f3
--- /dev/null
+++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift
@@ -0,0 +1,37 @@
+/*
+ Copyright 2019 New Vector Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import Foundation
+
+@objcMembers
+final class RoomContextualMenuItem: NSObject {
+
+ // MARK: - Properties
+
+ let title: String
+ let image: UIImage?
+
+ var isEnabled: Bool = true
+ var action: (() -> Void)?
+
+ // MARK: - Setup
+
+ init(menuAction: RoomContextualMenuAction) {
+ self.title = menuAction.title
+ self.image = menuAction.image
+ super.init()
+ }
+}
diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift
new file mode 100644
index 000000000..c4c389372
--- /dev/null
+++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift
@@ -0,0 +1,141 @@
+/*
+ Copyright 2019 New Vector Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import UIKit
+import Reusable
+
+final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoadable, Themable {
+
+ // MARK: - Constants
+
+ private enum Constants {
+ static let menuItemMinWidth: CGFloat = 50.0
+ static let menuItemMaxWidth: CGFloat = 80.0
+ }
+
+ // MARK: - Properties
+
+ // MARK: Outlets
+
+ @IBOutlet private weak var menuItemsStackView: UIStackView!
+ @IBOutlet private weak var separatorView: UIView!
+
+ // MARK: Private
+
+ private var theme: Theme?
+ private var menuItemViews: [ContextualMenuItemView] = []
+
+ // MARK: - Public
+
+ @objc func update(theme: Theme) {
+ self.theme = theme
+ self.backgroundColor = theme.backgroundColor
+ self.tintColor = theme.tintColor
+ self.separatorView.backgroundColor = theme.lineBreakColor
+
+ for menuItemView in self.menuItemViews {
+ menuItemView.titleColor = theme.textPrimaryColor
+ menuItemView.imageColor = theme.tintColor
+ }
+ }
+
+ @objc func fill(contextualMenuItems: [RoomContextualMenuItem]) {
+ self.menuItemsStackView.vc_removeAllSubviews()
+ self.menuItemViews.removeAll()
+
+ for menuItem in contextualMenuItems {
+ let menuItemView = ContextualMenuItemView()
+ menuItemView.fill(menuItem: menuItem)
+
+ if let theme = theme {
+ menuItemView.titleColor = theme.textPrimaryColor
+ menuItemView.imageColor = theme.tintColor
+ }
+
+ self.add(menuItemView: menuItemView)
+ }
+
+ self.layoutIfNeeded()
+ }
+
+ // MARK: - Setup
+
+ private func commonInit() {
+ }
+
+ convenience init() {
+ self.init(frame: CGRect.zero)
+ self.loadNibContent()
+ commonInit()
+ }
+
+ required init?(coder aDecoder: NSCoder) {
+ super.init(coder: aDecoder)
+ self.loadNibContent()
+ commonInit()
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ self.loadNibContent()
+ commonInit()
+ }
+
+ // MARK: - Life cycle
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ }
+
+ // MARK: - Private
+
+ private func add(menuItemView: ContextualMenuItemView) {
+ let menuItemContentView = UIView()
+ menuItemContentView.backgroundColor = .clear
+
+ self.add(menuItemView: menuItemView, on: menuItemContentView)
+
+ self.menuItemsStackView.addArrangedSubview(menuItemContentView)
+
+ let widthConstraint = menuItemContentView.widthAnchor.constraint(equalTo: self.menuItemsStackView.widthAnchor)
+ widthConstraint.priority = .defaultLow
+ widthConstraint.isActive = true
+
+ self.menuItemViews.append(menuItemView)
+ }
+
+ private func add(menuItemView: ContextualMenuItemView, on contentView: UIView) {
+ contentView.translatesAutoresizingMaskIntoConstraints = false
+ menuItemView.translatesAutoresizingMaskIntoConstraints = false
+
+ contentView.addSubview(menuItemView)
+
+ menuItemView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
+ menuItemView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
+
+ let widthConstraint = menuItemView.widthAnchor.constraint(equalToConstant: 0.0)
+ widthConstraint.priority = .defaultLow
+ widthConstraint.isActive = true
+
+ let minWidthConstraint = menuItemView.widthAnchor.constraint(greaterThanOrEqualToConstant: Constants.menuItemMinWidth)
+ minWidthConstraint.priority = .required
+ minWidthConstraint.isActive = true
+
+ let maxWidthConstraint = menuItemView.widthAnchor.constraint(lessThanOrEqualToConstant: Constants.menuItemMaxWidth)
+ maxWidthConstraint.priority = .required
+ maxWidthConstraint.isActive = true
+ }
+}
diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib
new file mode 100644
index 000000000..42b2b9ab3
--- /dev/null
+++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+