]> cat aescling's git repositories - mastodon.git/blob - app/javascript/glitch/components/status/header.js
bdb868e4d998fd8b0beacb2f46f0bb7daf620406
[mastodon.git] / app / javascript / glitch / components / status / header.js
1 /*
2
3 `<StatusHeader>`
4 ================
5
6 Originally a part of `<Status>`, but extracted into a separate
7 component for better documentation and maintainance by
8 @kibi@glitch.social as a part of glitch-soc/mastodon.
9
10 */
11
12 /* * * * */
13
14 /*
15
16 Imports:
17 --------
18
19 */
20
21 // Package imports //
22 import React from 'react';
23 import PropTypes from 'prop-types';
24 import ImmutablePropTypes from 'react-immutable-proptypes';
25 import { defineMessages, injectIntl } from 'react-intl';
26
27 // Mastodon imports //
28 import Avatar from '../../../mastodon/components/avatar';
29 import AvatarOverlay from '../../../mastodon/components/avatar_overlay';
30 import DisplayName from '../../../mastodon/components/display_name';
31 import IconButton from '../../../mastodon/components/icon_button';
32 import VisibilityIcon from './visibility_icon';
33
34 /* * * * */
35
36 /*
37
38 Inital setup:
39 -------------
40
41 The `messages` constant is used to define any messages that we need
42 from inside props. In our case, these are the `collapse` and
43 `uncollapse` messages used with our collapse/uncollapse buttons.
44
45 */
46
47 const messages = defineMessages({
48 collapse: { id: 'status.collapse', defaultMessage: 'Collapse' },
49 uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' },
50 public: { id: 'privacy.public.short', defaultMessage: 'Public' },
51 unlisted: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
52 private: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
53 direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
54 });
55
56 /* * * * */
57
58 /*
59
60 The `<StatusHeader>` component:
61 -------------------------------
62
63 The `<StatusHeader>` component wraps together the header information
64 (avatar, display name) and upper buttons and icons (collapsing, media
65 icons) into a single `<header>` element.
66
67 ### Props
68
69 - __`account`, `friend` (`ImmutablePropTypes.map`) :__
70 These give the accounts associated with the status. `account` is
71 the author of the post; `friend` will have their avatar appear
72 in the overlay if provided.
73
74 - __`mediaIcon` (`PropTypes.string`) :__
75 If a mediaIcon should be placed in the header, this string
76 specifies it.
77
78 - __`collapsible`, `collapsed` (`PropTypes.bool`) :__
79 These props tell whether a post can be, and is, collapsed.
80
81 - __`parseClick` (`PropTypes.func`) :__
82 This function will be called when the user clicks inside the header
83 information.
84
85 - __`setExpansion` (`PropTypes.func`) :__
86 This function is used to set the expansion state of the post.
87
88 - __`intl` (`PropTypes.object`) :__
89 This is our internationalization object, provided by
90 `injectIntl()`.
91
92 */
93
94 @injectIntl
95 export default class StatusHeader extends React.PureComponent {
96
97 static propTypes = {
98 status: ImmutablePropTypes.map.isRequired,
99 friend: ImmutablePropTypes.map,
100 mediaIcon: PropTypes.string,
101 collapsible: PropTypes.bool,
102 collapsed: PropTypes.bool,
103 parseClick: PropTypes.func.isRequired,
104 setExpansion: PropTypes.func.isRequired,
105 intl: PropTypes.object.isRequired,
106 };
107
108 /*
109
110 ### Implementation
111
112 #### `handleCollapsedClick()`.
113
114 `handleCollapsedClick()` is just a simple callback for our collapsing
115 button. It calls `setExpansion` to set the collapsed state of the
116 status.
117
118 */
119
120 handleCollapsedClick = (e) => {
121 const { collapsed, setExpansion } = this.props;
122 if (e.button === 0) {
123 setExpansion(collapsed ? null : false);
124 e.preventDefault();
125 }
126 }
127
128 /*
129
130 #### `handleAccountClick()`.
131
132 `handleAccountClick()` handles any clicks on the header info. It calls
133 `parseClick()` with our `account` as the anticipatory `destination`.
134
135 */
136
137 handleAccountClick = (e) => {
138 const { status, parseClick } = this.props;
139 parseClick(e, `/accounts/${+status.getIn(['account', 'id'])}`);
140 }
141
142 /*
143
144 #### `render()`.
145
146 `render()` actually puts our element on the screen. `<StatusHeader>`
147 has a very straightforward rendering process.
148
149 */
150
151 render () {
152 const {
153 status,
154 friend,
155 mediaIcon,
156 collapsible,
157 collapsed,
158 intl,
159 } = this.props;
160
161 const account = status.get('account');
162
163 return (
164 <header className='status__info'>
165 {
166
167 /*
168
169 We have to include the status icons before the header content because
170 it is rendered as a float.
171
172 */
173
174 }
175 <div className='status__info__icons'>
176 {mediaIcon ? (
177 <i
178 className={`fa fa-fw fa-${mediaIcon}`}
179 aria-hidden='true'
180 />
181 ) : null}
182 {(
183 <VisibilityIcon visibility={status.get('visibility')} />
184 )}
185 {collapsible ? (
186 <IconButton
187 className='status__collapse-button'
188 animate flip
189 active={collapsed}
190 title={
191 collapsed ?
192 intl.formatMessage(messages.uncollapse) :
193 intl.formatMessage(messages.collapse)
194 }
195 icon='angle-double-up'
196 onClick={this.handleCollapsedClick}
197 />
198 ) : null}
199 </div>
200 {
201
202 /*
203
204 This begins our header content. It is all wrapped inside of a link
205 which gets handled by `handleAccountClick`. We use an `<AvatarOverlay>`
206 if we have a `friend` and a normal `<Avatar>` if we don't.
207
208 */
209
210 }
211 <a
212 href={account.get('url')}
213 target='_blank'
214 className='status__display-name'
215 onClick={this.handleAccountClick}
216 >
217 <div className='status__avatar'>{
218 friend ? (
219 <AvatarOverlay account={account} friend={friend} />
220 ) : (
221 <Avatar account={account} size={48} />
222 )
223 }</div>
224 <DisplayName account={account} />
225 </a>
226
227 </header>
228 );
229 }
230
231 }
This page took 0.087507 seconds and 2 git commands to generate.