Skip to content

Commit a4b8a36

Browse files
committed
rustla-side implementation of modal image preview and added dev-server starting command for windows machines
1 parent e11911b commit a4b8a36

9 files changed

Lines changed: 143 additions & 1 deletion

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
},
99
"scripts": {
1010
"clean": "rm -rf public/assets public/index.html",
11+
"clean-w": "rmdir /s /q public\\assets && del /f /q public\\index.html",
1112
"build": "npm run clean && npm run build:bundle",
13+
"build-w": "npm run clean-w && npm run build:bundle",
1214
"build:bundle": "webpack --progress",
1315
"build:production": "npm run --prod build",
1416
"dev-server": "npm run build && webpack-dev-server --inline --hot",
17+
"dev-server-w": "npm run build-w && webpack-dev-server --inline --hot",
1518
"flow": "flow check",
1619
"lint": "eslint webpack.config.js --ext .js --ext .jsx src"
1720
},

src/INITIAL_STATE.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default {
1313
chatSize: localStorage ? Number(localStorage.getItem('chatSize')) || 400 : 400,
1414
showChat: localStorage ? !(localStorage.getItem('showChat') === 'false') : true,
1515
showHeader: true,
16+
imageModalSrc: null,
1617
showFooter: true,
1718
},
1819
self: {

src/actions/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ export const showHeader = value => dispatch => {
175175
});
176176
};
177177

178+
export const IMAGE_MODAL_SRC = Symbol('IMAGE_MODAL_SRC');
179+
export const imageModalSrc = value => dispatch => {
180+
dispatch({
181+
type: IMAGE_MODAL_SRC,
182+
payload: value,
183+
});
184+
};
185+
178186
export const SHOW_FOOTER = Symbol('SHOW_FOOTER');
179187
export const showFooter = value => dispatch => {
180188
dispatch({
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// @flow
2+
3+
import React from "react";
4+
import cs from "classnames";
5+
6+
import "../css/ImageModal";
7+
import { connect } from "react-redux";
8+
import { imageModalSrc } from "../actions";
9+
10+
const ImageModal = ({ dispatch, src, onClose }) => (
11+
<div
12+
className="image-modal"
13+
tabIndex={-1}
14+
onClick={onClose}
15+
onKeyDown={(e) => {
16+
if (e.key === "Escape") dispatch(imageModalSrc(null));
17+
}}
18+
>
19+
<div
20+
className="image-modal__backdrop"
21+
onClick={() => {
22+
dispatch(imageModalSrc(null));
23+
}}
24+
></div>
25+
26+
<div className="image-modal__content" onClick={(e) => e.stopPropagation()}>
27+
<div className="image-modal__toolbar">
28+
<button
29+
className="image-modal__btn button-orange"
30+
href={src}
31+
tabIndex={0}
32+
target="_blank"
33+
>
34+
<span className="glyphicon glyphicon-new-window" />
35+
</button>
36+
<button
37+
className="image-modal__btn button-orange"
38+
tabIndex={0}
39+
onClick={() => navigator.clipboard.writeText(src)}
40+
>
41+
<span className="glyphicon glyphicon-duplicate" />
42+
</button>
43+
44+
<button
45+
className="image-modal__btn image-modal__btn--close button-orange"
46+
aria-label="Close"
47+
onClick={onClose}
48+
tabIndex={0}
49+
autoFocus
50+
>
51+
<span className="glyphicon glyphicon-remove" />
52+
</button>
53+
</div>
54+
55+
<img src={src} alt="" />
56+
</div>
57+
</div>
58+
);
59+
60+
export default connect()(ImageModal);

src/components/RoutesWithChat.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Header from './Header';
1313
import Footer from './Footer';
1414

1515
import CustomScrollbar from './CustomScrollbar';
16+
import ImageModal from './ImagePreviewModal';
1617

1718
import '../css/Stream';
1819

@@ -22,7 +23,7 @@ import {
2223
showHeader as headerFunc
2324
} from '../actions';
2425

25-
export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, showLeftChat=false, chatClosed, chatSize, headerFunc}) =>
26+
export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, showLeftChat=false, chatClosed, chatSize, headerFunc, imageModalSrc}) =>
2627
{
2728
let left = (
2829
<div className='flex-shrink-0 stream-embed' style={{ width: chatClosed ? '100%' : `calc(100% - ${chatSize}px)`, height: chatClosed ? '100%' : '', display: 'flex', flexDirection: 'column'}}>
@@ -53,6 +54,11 @@ export const RoutesWithChat = ({showHeader, showFooter, setChatSize, showChat, s
5354
<div title={showHeader ? "Close Header" : "Open Header"} className= {showHeader ?"close-header-btn" : 'open-header-btn'}>
5455
<span className={showHeader ? 'close-header-caret': 'open-header-caret'} onClick={() => showHeader ? headerFunc(false) : headerFunc(true)}>&#8250;</span>
5556
</div>
57+
{imageModalSrc && (
58+
<ImageModal
59+
src={imageModalSrc}
60+
/>
61+
)}
5662
<Resizeable
5763
className='flex-grow-1 flex-column flex-lg-row'
5864
onResize={e => {
@@ -80,6 +86,8 @@ RoutesWithChat.propTypes = {
8086
showLeftChat: PropTypes.bool,
8187
chatClosed: PropTypes.bool,
8288

89+
imageModalSrc: PropTypes.string,
90+
8391
chatSize: PropTypes.number.isRequired,
8492

8593
setChatSize: PropTypes.func.isRequired,
@@ -96,6 +104,7 @@ export default compose(
96104
showLeftChat: idx(state, _ => _.self.profile.data.left_chat),
97105
chatClosed: !state.ui.showChat || !state.self.isLoggedIn,
98106
headerClosed: !state.ui.showHeader,
107+
imageModalSrc: state.ui.imageModalSrc,
99108
}),
100109
{
101110
setChatSize,

src/css/ImageModal.scss

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// modal when image is opened
2+
.image-modal {
3+
position: fixed;
4+
inset: 0;
5+
z-index: 9999;
6+
}
7+
8+
.image-modal__backdrop {
9+
position: absolute;
10+
inset: 0;
11+
background: rgba(0, 0, 0, 0.8);
12+
}
13+
14+
.image-modal__content {
15+
height: 100%;
16+
place-items: end;
17+
margin-left: auto;
18+
margin-right: auto;
19+
display: flex;
20+
justify-content: center;
21+
flex-flow: column;
22+
width: max-content;
23+
z-index: 100;
24+
position: relative;
25+
}
26+
27+
.image-modal__btn {
28+
margin-left: 15px;
29+
cursor: pointer;
30+
}
31+
32+
.image-modal__toolbar {
33+
margin-bottom: 10px;
34+
button {
35+
span {
36+
font-size: 25px;
37+
}
38+
}
39+
}
40+
41+
.image-modal img {
42+
max-width: 90vw;
43+
max-height: 90vh;
44+
user-select: none;
45+
}

src/css/main.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ a {
6060
}
6161
}
6262

63+
.button-orange{
64+
border: none;
65+
background-color: transparent;
66+
color: $rustle-orange;
67+
&:hover, &:focus {
68+
color: darken($rustle-orange, 20%);
69+
}
70+
}
71+
6372
.form-control, .form-control, .btn-group > .navbar-btn, .input-group-btn > .btn {
6473
background-color: #222;
6574
color: #fff;

src/reducers/ui.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SHOW_CHAT,
77
SHOW_HEADER,
88
SHOW_FOOTER,
9+
IMAGE_MODAL_SRC,
910
} from '../actions';
1011
import { actions } from '../actions/websocket';
1112

@@ -38,6 +39,11 @@ function uiReducer(state = INITIAL_STATE.ui, action) {
3839
...state,
3940
showHeader: action.payload,
4041
};
42+
case IMAGE_MODAL_SRC:
43+
return {
44+
...state,
45+
imageModalSrc: action.payload,
46+
};
4147
case SHOW_FOOTER:
4248
return {
4349
...state,

src/redux/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ export type State = {|
5151
+showChat: boolean,
5252
+showHeader: boolean,
5353
+showFooter: boolean,
54+
+imageModalSrc: string,
5455
|}
5556
|};

0 commit comments

Comments
 (0)