Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.

Commit 5225f48

Browse files
authored
Merge pull request #2568 from withspectrum/upgrade-community-notification-preferences
Upgrade community notification preferences
2 parents 679f310 + b0af9cb commit 5225f48

File tree

10 files changed

+182
-73
lines changed

10 files changed

+182
-73
lines changed

src/components/listItems/style.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const Row = styled(FlexRow)`
6262

6363
export const Heading = styled(H3)`
6464
font-weight: 500;
65-
font-size: 14px;
65+
font-size: 16px;
6666
transition: ${Transition.hover.off};
6767
line-height: 1.2;
6868
display: flex;
@@ -96,8 +96,6 @@ export const ActionContainer = styled(FlexCol)`
9696

9797
export const StyledCard = styled.div`
9898
flex-direction: column;
99-
margin-top: 32px;
100-
margin-left: 32px;
10199
display: ${props => (props.smallOnly ? 'none' : 'flex')};
102100
103101
@media (max-width: 768px) {

src/components/profile/style.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { Button, OutlineButton, IconButton } from '../buttons';
1313
import { ReputationWrapper } from '../reputation/style';
1414
import Card from '../card';
15-
import { Heading } from '../listItems/style';
1615

1716
export const ProfileHeader = styled(FlexRow)`
1817
padding: 16px;
@@ -78,11 +77,6 @@ export const FullProfile = styled.div`
7877
margin-top: -64px;
7978
background-color: ${props => props.theme.bg.default};
8079
81-
${Heading} {
82-
font-size: 16px;
83-
color: ${props => props.theme.text.alt};
84-
}
85-
8680
@media (max-width: 768px) {
8781
margin-top: -48px;
8882
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// @flow
2+
import * as React from 'react';
3+
import { connect } from 'react-redux';
4+
import compose from 'recompose/compose';
5+
import { addToastWithTimeout } from '../../actions/toasts';
6+
import type { GetChannelType } from 'shared/graphql/queries/channel/getChannel';
7+
import toggleChannelNotificationsMutation, {
8+
type ToggleChannelNotificationsType,
9+
} from 'shared/graphql/mutations/channel/toggleChannelNotifications';
10+
11+
type Props = {
12+
channel: {
13+
...$Exact<GetChannelType>,
14+
},
15+
toggleChannelNotifications: Function,
16+
dispatch: Function,
17+
render: Function,
18+
};
19+
20+
type State = { isLoading: boolean };
21+
22+
class ToggleChannelNotifications extends React.Component<Props, State> {
23+
state = { isLoading: false };
24+
25+
init = () => {
26+
this.setState({
27+
isLoading: true,
28+
});
29+
30+
return this.toggleNotifications();
31+
};
32+
33+
terminate = () => {
34+
this.setState({
35+
isLoading: false,
36+
});
37+
};
38+
39+
toggleNotifications = () => {
40+
const { channel } = this.props;
41+
42+
this.setState({
43+
isLoading: true,
44+
});
45+
46+
this.props
47+
.toggleChannelNotifications(channel.id)
48+
.then(({ data }: ToggleChannelNotificationsType) => {
49+
this.setState({
50+
isLoading: false,
51+
});
52+
53+
const { toggleChannelNotifications } = data;
54+
55+
const value =
56+
toggleChannelNotifications.channelPermissions.receiveNotifications;
57+
const type = value ? 'success' : 'neutral';
58+
const str = value
59+
? 'Notifications activated!'
60+
: 'Notifications turned off.';
61+
this.props.dispatch(addToastWithTimeout(type, str));
62+
return;
63+
})
64+
.catch(err => {
65+
this.setState({
66+
isLoading: false,
67+
});
68+
this.props.dispatch(addToastWithTimeout('error', err.message));
69+
});
70+
};
71+
72+
render() {
73+
return <div onClick={this.init}>{this.props.render(this.state)}</div>;
74+
}
75+
}
76+
77+
export default compose(connect(), toggleChannelNotificationsMutation)(
78+
ToggleChannelNotifications
79+
);

src/components/upsell/joinChannel.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import toggleChannelSubscriptionMutation from 'shared/graphql/mutations/channel/
66
import type { ToggleChannelSubscriptionType } from 'shared/graphql/mutations/channel/toggleChannelSubscription';
77
import { addToastWithTimeout } from '../../actions/toasts';
88
import { track } from '../../helpers/events';
9-
import { NullState } from './index';
109
import {
11-
Title,
12-
Subtitle,
1310
JoinChannelContainer,
1411
JoinChannelContent,
1512
JoinChannelTitle,

src/views/community/components/channelList.js

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@ import { connect } from 'react-redux';
55
import compose from 'recompose/compose';
66
import { ChannelListItem } from '../../../components/listItems';
77
import { ChannelProfile } from '../../../components/profile';
8-
import { IconButton } from '../../../components/buttons';
8+
import { OutlineButton } from '../../../components/buttons';
99
import Icon from '../../../components/icons';
1010
import { openModal } from '../../../actions/modals';
1111
import viewNetworkHandler from '../../../components/viewNetworkHandler';
12-
import { LoadingCard } from '../../../components/loading';
12+
import { LoadingCard, Loading } from '../../../components/loading';
1313
import getCommunityChannels from 'shared/graphql/queries/community/getCommunityChannelConnection';
1414
import type { GetCommunityChannelConnectionType } from 'shared/graphql/queries/community/getCommunityChannelConnection';
15+
import { StyledCard, ListContainer } from '../../../components/listItems/style';
1516
import {
16-
StyledCard,
17-
ListHeader,
18-
ListHeading,
19-
ListContainer,
20-
} from '../../../components/listItems/style';
17+
ColumnHeading,
18+
ChannelListItemRow,
19+
ToggleNotificationsContainer,
20+
} from '../style';
21+
import ToggleChannelNotifications from 'src/components/toggleChannelNotifications';
2122

2223
type Props = {
2324
data: {
@@ -80,18 +81,7 @@ class ChannelList extends React.Component<Props> {
8081

8182
return (
8283
<StyledCard largeOnly>
83-
<ListHeader>
84-
<ListHeading>Channels</ListHeading>
85-
{isOwner && (
86-
<IconButton
87-
glyph="plus"
88-
color="text.placeholder"
89-
onClick={() =>
90-
dispatch(openModal('CREATE_CHANNEL_MODAL', community))
91-
}
92-
/>
93-
)}
94-
</ListHeader>
84+
<ColumnHeading>Channels</ColumnHeading>
9585

9686
{/*
9787
user isn't logged in, channel list is used for navigation
@@ -128,31 +118,57 @@ class ChannelList extends React.Component<Props> {
128118
{sortedJoinedChannels.map(channel => {
129119
if (!channel) return null;
130120
return (
131-
<Link
132-
key={channel.id}
133-
to={`/${communitySlug}/${channel.slug}`}
134-
>
135-
<ChannelListItem
136-
clickable
137-
contents={channel}
138-
withDescription={false}
139-
channelIcon
121+
<ChannelListItemRow key={channel.id}>
122+
<Link
123+
to={`/${communitySlug}/${channel.slug}`}
124+
style={{
125+
display: 'flex',
126+
flex: 'auto',
127+
}}
140128
>
141-
<Icon glyph="view-forward" />
142-
</ChannelListItem>
143-
</Link>
129+
<ChannelListItem
130+
clickable
131+
contents={channel}
132+
withDescription={false}
133+
channelIcon
134+
/>
135+
</Link>
136+
<ToggleChannelNotifications
137+
channel={channel}
138+
render={state => (
139+
<ToggleNotificationsContainer
140+
tipLocation={'top-left'}
141+
tipText={
142+
channel.channelPermissions.receiveNotifications
143+
? 'Turn notifications off'
144+
: 'Turn notifications on'
145+
}
146+
>
147+
{state.isLoading ? (
148+
<Loading />
149+
) : (
150+
<Icon
151+
glyph={
152+
channel.channelPermissions
153+
.receiveNotifications
154+
? 'notification-fill'
155+
: 'notification'
156+
}
157+
/>
158+
)}
159+
</ToggleNotificationsContainer>
160+
)}
161+
/>
162+
</ChannelListItemRow>
144163
);
145164
})}
146165
</ListContainer>
147166
)}
148167

149168
{sortedNonJoinedChannels.length > 0 &&
150169
isMember && (
151-
<span>
152-
<ListHeader secondary>
153-
<ListHeading>New channels</ListHeading>
154-
</ListHeader>
155-
170+
<React.Fragment>
171+
<ColumnHeading>New channels</ColumnHeading>
156172
<ListContainer>
157173
<ul>
158174
{sortedNonJoinedChannels.map(channel => {
@@ -167,8 +183,23 @@ class ChannelList extends React.Component<Props> {
167183
})}
168184
</ul>
169185
</ListContainer>
170-
</span>
186+
</React.Fragment>
171187
)}
188+
189+
{isOwner && (
190+
<OutlineButton
191+
style={{
192+
alignSelf: 'flex-end',
193+
marginTop: '12px',
194+
}}
195+
glyph="plus"
196+
onClick={() =>
197+
dispatch(openModal('CREATE_CHANNEL_MODAL', community))
198+
}
199+
>
200+
Create a channel
201+
</OutlineButton>
202+
)}
172203
</StyledCard>
173204
);
174205
}

src/views/community/components/memberGrid.js

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import compose from 'recompose/compose';
44
import getCommunityMembersQuery, {
55
type GetCommunityMembersType,
66
} from 'shared/graphql/queries/community/getCommunityMembers';
7-
import { FlexCol } from '../../../components/globals';
8-
import { Card } from '../../../components/card';
9-
import { LoadingList } from '../../../components/loading';
10-
import { UserListItem } from '../../../components/listItems';
11-
import viewNetworkHandler from '../../../components/viewNetworkHandler';
12-
import ViewError from '../../../components/viewError';
13-
import { StyledButton } from '../style';
7+
import { FlexCol } from 'src/components/globals';
8+
import { Card } from 'src/components/card';
9+
import { LoadingList } from 'src/components/loading';
10+
import { UserListItem } from 'src/components/listItems';
11+
import viewNetworkHandler from 'src/components/viewNetworkHandler';
12+
import ViewError from 'src/components/viewError';
1413

1514
type Props = {
1615
data: {
@@ -23,11 +22,7 @@ type Props = {
2322

2423
class CommunityMemberGrid extends React.Component<Props> {
2524
render() {
26-
const {
27-
data: { community, fetchMore },
28-
isLoading,
29-
isFetchingMore,
30-
} = this.props;
25+
const { data: { community }, isLoading } = this.props;
3126

3227
if (community) {
3328
const { edges: members } = community.members;
@@ -45,11 +40,6 @@ class CommunityMemberGrid extends React.Component<Props> {
4540
});
4641
return <UserListItem key={user.id} user={user} />;
4742
})}
48-
{community.members.pageInfo.hasNextPage && (
49-
<StyledButton loading={isFetchingMore} onClick={() => fetchMore()}>
50-
View more...
51-
</StyledButton>
52-
)}
5343
</FlexCol>
5444
);
5545
}

src/views/community/index.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,6 @@ class CommunityView extends React.Component<Props, State> {
223223
</LoginButton>
224224
</Link>
225225
)}
226-
<ChannelList
227-
id={community.id}
228-
communitySlug={communitySlug.toLowerCase()}
229-
/>
230226
</Meta>
231227
<Content>
232228
<SegmentedControl style={{ margin: '16px 0 0 0' }}>
@@ -309,7 +305,17 @@ class CommunityView extends React.Component<Props, State> {
309305
selectedView === 'search' && <Search community={community} />}
310306
</Content>
311307
<Extras>
312-
<ColumnHeading>Members</ColumnHeading>
308+
<ChannelList
309+
id={community.id}
310+
communitySlug={communitySlug.toLowerCase()}
311+
/>
312+
313+
<ColumnHeading>
314+
Top Members ({community.metaData &&
315+
community.metaData.members &&
316+
community.metaData.members.toLocaleString()}{' '}
317+
total)
318+
</ColumnHeading>
313319
<CommunityMemberGrid first={5} id={community.id} />
314320
</Extras>
315321
</Grid>

src/views/community/style.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Transition,
88
zIndex,
99
Gradient,
10+
Tooltip,
1011
} from '../../components/globals';
1112
import {
1213
DesktopSegment,
@@ -222,3 +223,17 @@ export const ColumnHeading = styled.div`
222223
margin-top: 16px;
223224
border-bottom: 2px solid ${props => props.theme.bg.border};
224225
`;
226+
227+
export const ChannelListItemRow = styled.div`
228+
display: flex;
229+
`;
230+
231+
export const ToggleNotificationsContainer = styled.div`
232+
display: flex;
233+
color: ${props => props.theme.text.alt};
234+
justify-content: center;
235+
align-items: center;
236+
height: 100%;
237+
cursor: pointer;
238+
${Tooltip};
239+
`;

src/views/status/style.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// @flow
22
import styled from 'styled-components';
3-
import { hexa } from 'src/components/globals';
43

54
export const Bar = styled.div`
65
display: grid;

src/views/thread/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
getThreadByMatch,
1818
getThreadByMatchQuery,
1919
} from 'shared/graphql/queries/thread/getThread';
20-
import { NullState, UpsellSignIn } from '../../components/upsell';
20+
import { NullState } from '../../components/upsell';
2121
import JoinChannel from '../../components/upsell/joinChannel';
2222
import { toState } from 'shared/draft-utils';
2323
import LoadingView from './components/loading';

0 commit comments

Comments
 (0)