diff --git a/package.json b/package.json
index 0b879dd8..d94394cd 100644
--- a/package.json
+++ b/package.json
@@ -8,10 +8,13 @@
},
"scripts": {
"clean": "rm -rf public/assets public/index.html",
+ "clean-w": "rmdir /s /q public\\assets && del /f /q public\\index.html",
"build": "npm run clean && npm run build:bundle",
+ "build-w": "npm run clean-w && npm run build:bundle",
"build:bundle": "webpack --progress",
"build:production": "npm run --prod build",
"dev-server": "npm run build && webpack-dev-server --inline --hot",
+ "dev-server-w": "npm run build-w && webpack-dev-server --inline --hot",
"flow": "flow check",
"lint": "eslint webpack.config.js --ext .js --ext .jsx src"
},
diff --git a/src/INITIAL_STATE.js b/src/INITIAL_STATE.js
index be3541bd..e0ee34f8 100644
--- a/src/INITIAL_STATE.js
+++ b/src/INITIAL_STATE.js
@@ -13,6 +13,7 @@ export default {
chatSize: localStorage ? Number(localStorage.getItem('chatSize')) || 400 : 400,
showChat: localStorage ? !(localStorage.getItem('showChat') === 'false') : true,
showHeader: true,
+ imageModalSrc: null,
showFooter: true,
},
self: {
diff --git a/src/actions/index.js b/src/actions/index.js
index 26c45c36..7803a371 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -175,6 +175,14 @@ export const showHeader = value => dispatch => {
});
};
+export const IMAGE_MODAL_SRC = Symbol('IMAGE_MODAL_SRC');
+export const imageModalSrc = value => dispatch => {
+ dispatch({
+ type: IMAGE_MODAL_SRC,
+ payload: value,
+ });
+};
+
export const SHOW_FOOTER = Symbol('SHOW_FOOTER');
export const showFooter = value => dispatch => {
dispatch({
diff --git a/src/components/ImagePreviewModal.jsx b/src/components/ImagePreviewModal.jsx
new file mode 100644
index 00000000..8f030667
--- /dev/null
+++ b/src/components/ImagePreviewModal.jsx
@@ -0,0 +1,63 @@
+// @flow
+
+import React from "react";
+import cs from "classnames";
+
+import "../css/ImageModal";
+import { connect } from "react-redux";
+import { imageModalSrc } from "../actions";
+
+const ImageModal = ({ dispatch, src, onClose }) => (
+
{
+ if (e.key === "Escape") dispatch(imageModalSrc(null));
+ }}
+ >
+
{
+ dispatch(imageModalSrc(null));
+ }}
+ >
+
+
e.stopPropagation()}>
+
+
+
+
+
+
+
+

+
+
+);
+
+export default connect()(ImageModal);
diff --git a/src/components/RoutesWithChat.jsx b/src/components/RoutesWithChat.jsx
index 107458f7..b0d44bd1 100644
--- a/src/components/RoutesWithChat.jsx
+++ b/src/components/RoutesWithChat.jsx
@@ -13,6 +13,7 @@ import Header from './Header';
import Footer from './Footer';
import CustomScrollbar from './CustomScrollbar';
+import ImageModal from './ImagePreviewModal';
import '../css/Stream';
@@ -22,7 +23,7 @@ import {
showHeader as headerFunc
} from '../actions';
-export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, showLeftChat=false, chatClosed, chatSize, headerFunc}) =>
+export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, showLeftChat=false, chatClosed, chatSize, headerFunc, imageModalSrc}) =>
{
let left = (
@@ -53,6 +54,11 @@ export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, s
showHeader ? headerFunc(false) : headerFunc(true)}>›
+ {imageModalSrc && (
+
+ )}
{
@@ -80,6 +86,8 @@ RoutesWithChat.propTypes = {
showLeftChat: PropTypes.bool,
chatClosed: PropTypes.bool,
+ imageModalSrc: PropTypes.string,
+
chatSize: PropTypes.number.isRequired,
setChatSize: PropTypes.func.isRequired,
@@ -96,6 +104,7 @@ export default compose(
showLeftChat: idx(state, _ => _.self.profile.data.left_chat),
chatClosed: !state.ui.showChat || !state.self.isLoggedIn,
headerClosed: !state.ui.showHeader,
+ imageModalSrc: state.ui.imageModalSrc,
}),
{
setChatSize,
diff --git a/src/css/ImageModal.scss b/src/css/ImageModal.scss
new file mode 100644
index 00000000..bebb05d0
--- /dev/null
+++ b/src/css/ImageModal.scss
@@ -0,0 +1,45 @@
+// modal when image is opened
+.image-modal {
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+}
+
+.image-modal__backdrop {
+ position: absolute;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.8);
+}
+
+.image-modal__content {
+ height: 100%;
+ place-items: end;
+ margin-left: auto;
+ margin-right: auto;
+ display: flex;
+ justify-content: center;
+ flex-flow: column;
+ width: max-content;
+ z-index: 100;
+ position: relative;
+}
+
+.image-modal__btn {
+ margin-left: 15px;
+ cursor: pointer;
+}
+
+.image-modal__toolbar {
+ margin-bottom: 10px;
+ button {
+ span {
+ font-size: 25px;
+ }
+ }
+}
+
+.image-modal img {
+ max-width: 90vw;
+ max-height: 90vh;
+ user-select: none;
+}
diff --git a/src/css/main.scss b/src/css/main.scss
index 52a1df9b..4b8562f5 100644
--- a/src/css/main.scss
+++ b/src/css/main.scss
@@ -60,6 +60,15 @@ a {
}
}
+.button-orange{
+ border: none;
+ background-color: transparent;
+ color: $rustle-orange;
+ &:hover, &:focus {
+ color: darken($rustle-orange, 20%);
+ }
+}
+
.form-control, .form-control, .btn-group > .navbar-btn, .input-group-btn > .btn {
background-color: #222;
color: #fff;
diff --git a/src/reducers/ui.js b/src/reducers/ui.js
index 92ba5569..93330e95 100644
--- a/src/reducers/ui.js
+++ b/src/reducers/ui.js
@@ -6,6 +6,7 @@ import {
SHOW_CHAT,
SHOW_HEADER,
SHOW_FOOTER,
+ IMAGE_MODAL_SRC,
} from '../actions';
import { actions } from '../actions/websocket';
@@ -38,6 +39,11 @@ function uiReducer(state = INITIAL_STATE.ui, action) {
...state,
showHeader: action.payload,
};
+ case IMAGE_MODAL_SRC:
+ return {
+ ...state,
+ imageModalSrc: action.payload,
+ };
case SHOW_FOOTER:
return {
...state,
diff --git a/src/redux/types.js b/src/redux/types.js
index a973a1f4..795b743a 100644
--- a/src/redux/types.js
+++ b/src/redux/types.js
@@ -51,5 +51,6 @@ export type State = {|
+showChat: boolean,
+showHeader: boolean,
+showFooter: boolean,
+ +imageModalSrc: string,
|}
|};