diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 6c85341aaf..2683a32dae 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -76,12 +76,56 @@ limitations under the License. // opacity: 0.5; position: relative; } + +.mx_TagPanel .mx_TagTile.mx_TagTile_prototype { + padding: 3px; +} + .mx_TagPanel .mx_TagTile:focus, .mx_TagPanel .mx_TagTile:hover, .mx_TagPanel .mx_TagTile.mx_TagTile_selected { // opacity: 1; } +.mx_TagPanel .mx_TagTile.mx_TagTile_selected_prototype { + background-color: $primary-bg-color; + border-radius: 6px; +} + +.mx_TagTile_selected_prototype { + .mx_TagTile_homeIcon::before { + background-color: $primary-fg-color; // dark-on-light + } +} + +.mx_TagTile:not(.mx_TagTile_selected_prototype) .mx_TagTile_homeIcon { + background-color: $icon-button-color; // XXX: Variable abuse + border-radius: 48px; + + &::before { + background-color: $primary-bg-color; // light-on-grey + } +} + +.mx_TagTile_homeIcon { + width: 32px; + height: 32px; + position: relative; + + &::before { + mask-image: url('$(res)/img/element-icons/home.svg'); + mask-position: center; + mask-repeat: no-repeat; + width: 21px; + height: 21px; + content: ''; + display: inline-block; + position: absolute; + top: calc(50% - 10.5px); + left: calc(50% - 10.5px); + } +} + .mx_TagPanel .mx_TagTile_plus { margin-bottom: 12px; height: 32px; @@ -116,10 +160,6 @@ limitations under the License. border-radius: 0 3px 3px 0; } -.mx_TagPanel .mx_TagTile.mx_TagTile_large.mx_TagTile_selected::before { - left: -10px; -} - .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { filter: none; } diff --git a/res/img/element-icons/home.svg b/res/img/element-icons/home.svg new file mode 100644 index 0000000000..d65812cafd --- /dev/null +++ b/res/img/element-icons/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 839d4bccda..30be71abcb 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -42,9 +42,6 @@ import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../views/context_menus/IconizedContextMenu"; -import TagOrderStore from "../../stores/TagOrderStore"; -import * as fbEmitter from "fbemitter"; -import FlairStore from "../../stores/FlairStore"; interface IProps { isMinimized: boolean; @@ -55,16 +52,11 @@ type PartialDOMRect = Pick; interface IState { contextMenuPosition: PartialDOMRect; isDarkTheme: boolean; - selectedCommunityProfile: { - displayName: string; - avatarMxc: string; - }; } export default class UserMenu extends React.Component { private dispatcherRef: string; private themeWatcherRef: string; - private tagStoreRef: fbEmitter.EventSubscription; private buttonRef: React.RefObject = createRef(); constructor(props: IProps) { @@ -73,7 +65,6 @@ export default class UserMenu extends React.Component { this.state = { contextMenuPosition: null, isDarkTheme: this.isUserOnDarkTheme(), - selectedCommunityProfile: null, }; OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); @@ -86,7 +77,6 @@ export default class UserMenu extends React.Component { public componentDidMount() { this.dispatcherRef = defaultDispatcher.register(this.onAction); this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged); - this.tagStoreRef = TagOrderStore.addListener(this.onTagStoreUpdate); } public componentWillUnmount() { @@ -103,25 +93,6 @@ export default class UserMenu extends React.Component { return theme === "dark"; } - private onTagStoreUpdate = async () => { - if (!SettingsStore.getValue("feature_communities_v2_prototypes")) { - return; - } - - const selectedId = TagOrderStore.getSelectedTags()[0]; - if (!selectedId) { - this.setState({selectedCommunityProfile: null}); - return; - } - - // For some reason the group's profile info isn't on the js-sdk Group object but - // is in the flair store, so get it from there. - const profile = await FlairStore.getGroupProfileCached(MatrixClientPeg.get(), selectedId); - const displayName = profile.name || selectedId; - const avatarMxc = profile.avatarUrl; - this.setState({selectedCommunityProfile: {displayName, avatarMxc}}); - }; - private onProfileUpdate = async () => { // the store triggered an update, so force a layout update. We don't // have any state to store here for that to magically happen. @@ -324,18 +295,8 @@ export default class UserMenu extends React.Component { public render() { const avatarSize = 32; // should match border-radius of the avatar - let displayName = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId(); - let avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); - - if (this.state.selectedCommunityProfile) { - displayName = this.state.selectedCommunityProfile.displayName - const mxc = this.state.selectedCommunityProfile.avatarMxc; - if (mxc) { - avatarUrl = MatrixClientPeg.get().mxcUrlToHttp(mxc, avatarSize, avatarSize); - } else { - avatarUrl = null; - } - } + const displayName = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId(); + const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); let name = {displayName}; let buttons = ( diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js index 49b336a577..db5eedc274 100644 --- a/src/components/views/elements/TagTile.js +++ b/src/components/views/elements/TagTile.js @@ -141,9 +141,12 @@ export default createReactClass({ profile.avatarUrl, avatarHeight, avatarHeight, "crop", ) : null; + const isPrototype = SettingsStore.getValue("feature_communities_v2_prototypes"); const className = classNames({ mx_TagTile: true, - mx_TagTile_selected: this.props.selected, + mx_TagTile_prototype: isPrototype, + mx_TagTile_selected: this.props.selected && !isPrototype, + mx_TagTile_selected_prototype: this.props.selected && isPrototype, }); const badge = TagOrderStore.getGroupBadge(this.props.tag); diff --git a/src/components/views/elements/UserTagTile.tsx b/src/components/views/elements/UserTagTile.tsx index c652423753..912f54edc7 100644 --- a/src/components/views/elements/UserTagTile.tsx +++ b/src/components/views/elements/UserTagTile.tsx @@ -16,16 +16,14 @@ limitations under the License. import React from "react"; import defaultDispatcher from "../../../dispatcher/dispatcher"; -import { OwnProfileStore } from "../../../stores/OwnProfileStore"; -import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import * as fbEmitter from "fbemitter"; import TagOrderStore from "../../../stores/TagOrderStore"; import AccessibleTooltipButton from "./AccessibleTooltipButton"; -import BaseAvatar from "../avatars/BaseAvatar"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; import classNames from "classnames"; +import { _t } from "../../../languageHandler"; -interface IProps{} +interface IProps { +} interface IState { selected: boolean; @@ -43,18 +41,13 @@ export default class UserTagTile extends React.PureComponent { } public componentDidMount() { - OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); this.tagStoreRef = TagOrderStore.addListener(this.onTagStoreUpdate); } public componentWillUnmount() { - OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); + this.tagStoreRef.remove(); } - private onProfileUpdate = () => { - this.forceUpdate(); - }; - private onTagStoreUpdate = () => { const selected = TagOrderStore.getSelectedTags().length === 0; this.setState({selected}); @@ -71,27 +64,20 @@ export default class UserTagTile extends React.PureComponent { public render() { // XXX: We reuse TagTile classes for ease of demonstration - we should probably generify // TagTile instead if we continue to use this component. - const avatarHeight = 36; - const name = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId(); const className = classNames({ mx_TagTile: true, - mx_TagTile_selected: this.state.selected, - mx_TagTile_large: true, + mx_TagTile_prototype: true, + mx_TagTile_selected_prototype: this.state.selected, + mx_TagTile_home: true, }); return (
- +
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f3cd1d80b7..d6ba736a76 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1556,6 +1556,7 @@ "And %(count)s more...|other": "And %(count)s more...", "ex. @bob:example.com": "ex. @bob:example.com", "Add User": "Add User", + "Home": "Home", "Enter a server name": "Enter a server name", "Looks good": "Looks good", "Can't find this server or its room list": "Can't find this server or its room list", @@ -2113,7 +2114,6 @@ "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s", "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", - "Home": "Home", "Switch to light mode": "Switch to light mode", "Switch to dark mode": "Switch to dark mode", "Switch theme": "Switch theme", diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js index 2b72a963b0..f02fce0769 100644 --- a/src/stores/TagOrderStore.js +++ b/src/stores/TagOrderStore.js @@ -166,6 +166,25 @@ class TagOrderStore extends Store { selectedTags: newTags, }); + if (!allowMultiple && newTags.length === 1) { + // We're in prototype behaviour: select the general chat for the community + const rooms = GroupStore.getGroupRooms(newTags[0]) + .map(r => MatrixClientPeg.get().getRoom(r.roomId)) + .filter(r => !!r); + let chat = rooms.find(r => { + const idState = r.currentState.getStateEvents("im.vector.general_chat", ""); + if (!idState || idState.getContent()['groupId'] !== newTags[0]) return false; + return true; + }); + if (!chat) chat = rooms[0]; + if (chat) { + dis.dispatch({ + action: 'view_room', + room_id: chat.roomId, + }); + } + } + Analytics.trackEvent('FilterStore', 'select_tag'); } break;