Merge pull request #5145 from matrix-org/travis/communities/proto/switcher
Communities v2 prototype: Tag panel selection changes
This commit is contained in:
commit
b294ec6427
7 changed files with 82 additions and 70 deletions
|
@ -76,12 +76,56 @@ limitations under the License.
|
||||||
// opacity: 0.5;
|
// opacity: 0.5;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_TagPanel .mx_TagTile.mx_TagTile_prototype {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TagPanel .mx_TagTile:focus,
|
.mx_TagPanel .mx_TagTile:focus,
|
||||||
.mx_TagPanel .mx_TagTile:hover,
|
.mx_TagPanel .mx_TagTile:hover,
|
||||||
.mx_TagPanel .mx_TagTile.mx_TagTile_selected {
|
.mx_TagPanel .mx_TagTile.mx_TagTile_selected {
|
||||||
// opacity: 1;
|
// 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 {
|
.mx_TagPanel .mx_TagTile_plus {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
@ -116,10 +160,6 @@ limitations under the License.
|
||||||
border-radius: 0 3px 3px 0;
|
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 {
|
.mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus {
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
|
|
3
res/img/element-icons/home.svg
Normal file
3
res/img/element-icons/home.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.33301 14.9494V7.28325C2.33301 6.8921 2.50477 6.52067 2.80281 6.26733L8.0282 1.82575C8.58834 1.34963 9.411 1.34963 9.97115 1.82575L15.1965 6.26734C15.4946 6.52067 15.6663 6.89065 15.6663 7.28181V14.9756C15.6663 16.0899 14.7592 16.9891 13.6449 16.9792C11.3214 16.9585 7.43567 16.9341 4.38152 16.9659C3.25744 16.9776 2.33301 16.0735 2.33301 14.9494Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 478 B |
|
@ -42,9 +42,6 @@ import IconizedContextMenu, {
|
||||||
IconizedContextMenuOption,
|
IconizedContextMenuOption,
|
||||||
IconizedContextMenuOptionList
|
IconizedContextMenuOptionList
|
||||||
} from "../views/context_menus/IconizedContextMenu";
|
} from "../views/context_menus/IconizedContextMenu";
|
||||||
import TagOrderStore from "../../stores/TagOrderStore";
|
|
||||||
import * as fbEmitter from "fbemitter";
|
|
||||||
import FlairStore from "../../stores/FlairStore";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
|
@ -55,16 +52,11 @@ type PartialDOMRect = Pick<DOMRect, "width" | "left" | "top" | "height">;
|
||||||
interface IState {
|
interface IState {
|
||||||
contextMenuPosition: PartialDOMRect;
|
contextMenuPosition: PartialDOMRect;
|
||||||
isDarkTheme: boolean;
|
isDarkTheme: boolean;
|
||||||
selectedCommunityProfile: {
|
|
||||||
displayName: string;
|
|
||||||
avatarMxc: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class UserMenu extends React.Component<IProps, IState> {
|
export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
private dispatcherRef: string;
|
private dispatcherRef: string;
|
||||||
private themeWatcherRef: string;
|
private themeWatcherRef: string;
|
||||||
private tagStoreRef: fbEmitter.EventSubscription;
|
|
||||||
private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
|
private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
|
@ -73,7 +65,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
contextMenuPosition: null,
|
contextMenuPosition: null,
|
||||||
isDarkTheme: this.isUserOnDarkTheme(),
|
isDarkTheme: this.isUserOnDarkTheme(),
|
||||||
selectedCommunityProfile: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
||||||
|
@ -86,7 +77,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
|
this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
|
||||||
this.tagStoreRef = TagOrderStore.addListener(this.onTagStoreUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
|
@ -103,25 +93,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
return theme === "dark";
|
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 () => {
|
private onProfileUpdate = async () => {
|
||||||
// the store triggered an update, so force a layout update. We don't
|
// the store triggered an update, so force a layout update. We don't
|
||||||
// have any state to store here for that to magically happen.
|
// have any state to store here for that to magically happen.
|
||||||
|
@ -324,18 +295,8 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
public render() {
|
public render() {
|
||||||
const avatarSize = 32; // should match border-radius of the avatar
|
const avatarSize = 32; // should match border-radius of the avatar
|
||||||
|
|
||||||
let displayName = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId();
|
const displayName = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId();
|
||||||
let avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize);
|
const 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = <span className="mx_UserMenu_userName">{displayName}</span>;
|
let name = <span className="mx_UserMenu_userName">{displayName}</span>;
|
||||||
let buttons = (
|
let buttons = (
|
||||||
|
|
|
@ -141,9 +141,12 @@ export default createReactClass({
|
||||||
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
const isPrototype = SettingsStore.getValue("feature_communities_v2_prototypes");
|
||||||
const className = classNames({
|
const className = classNames({
|
||||||
mx_TagTile: true,
|
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);
|
const badge = TagOrderStore.getGroupBadge(this.props.tag);
|
||||||
|
|
|
@ -16,16 +16,14 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||||
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
|
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
|
||||||
import * as fbEmitter from "fbemitter";
|
import * as fbEmitter from "fbemitter";
|
||||||
import TagOrderStore from "../../../stores/TagOrderStore";
|
import TagOrderStore from "../../../stores/TagOrderStore";
|
||||||
import AccessibleTooltipButton from "./AccessibleTooltipButton";
|
import AccessibleTooltipButton from "./AccessibleTooltipButton";
|
||||||
import BaseAvatar from "../avatars/BaseAvatar";
|
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
interface IProps{}
|
interface IProps {
|
||||||
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
@ -43,18 +41,13 @@ export default class UserTagTile extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
|
||||||
this.tagStoreRef = TagOrderStore.addListener(this.onTagStoreUpdate);
|
this.tagStoreRef = TagOrderStore.addListener(this.onTagStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate);
|
this.tagStoreRef.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onProfileUpdate = () => {
|
|
||||||
this.forceUpdate();
|
|
||||||
};
|
|
||||||
|
|
||||||
private onTagStoreUpdate = () => {
|
private onTagStoreUpdate = () => {
|
||||||
const selected = TagOrderStore.getSelectedTags().length === 0;
|
const selected = TagOrderStore.getSelectedTags().length === 0;
|
||||||
this.setState({selected});
|
this.setState({selected});
|
||||||
|
@ -71,27 +64,20 @@ export default class UserTagTile extends React.PureComponent<IProps, IState> {
|
||||||
public render() {
|
public render() {
|
||||||
// XXX: We reuse TagTile classes for ease of demonstration - we should probably generify
|
// XXX: We reuse TagTile classes for ease of demonstration - we should probably generify
|
||||||
// TagTile instead if we continue to use this component.
|
// TagTile instead if we continue to use this component.
|
||||||
const avatarHeight = 36;
|
|
||||||
const name = OwnProfileStore.instance.displayName || MatrixClientPeg.get().getUserId();
|
|
||||||
const className = classNames({
|
const className = classNames({
|
||||||
mx_TagTile: true,
|
mx_TagTile: true,
|
||||||
mx_TagTile_selected: this.state.selected,
|
mx_TagTile_prototype: true,
|
||||||
mx_TagTile_large: true,
|
mx_TagTile_selected_prototype: this.state.selected,
|
||||||
|
mx_TagTile_home: true,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
className={className}
|
className={className}
|
||||||
onClick={this.onTileClick}
|
onClick={this.onTileClick}
|
||||||
title={name}
|
title={_t("Home")}
|
||||||
>
|
>
|
||||||
<div className="mx_TagTile_avatar">
|
<div className="mx_TagTile_avatar">
|
||||||
<BaseAvatar
|
<div className="mx_TagTile_homeIcon" />
|
||||||
name={name}
|
|
||||||
idName={MatrixClientPeg.get().getUserId()}
|
|
||||||
url={OwnProfileStore.instance.getHttpAvatarUrl(avatarHeight)}
|
|
||||||
width={avatarHeight}
|
|
||||||
height={avatarHeight}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</AccessibleTooltipButton>
|
</AccessibleTooltipButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1556,6 +1556,7 @@
|
||||||
"And %(count)s more...|other": "And %(count)s more...",
|
"And %(count)s more...|other": "And %(count)s more...",
|
||||||
"ex. @bob:example.com": "ex. @bob:example.com",
|
"ex. @bob:example.com": "ex. @bob:example.com",
|
||||||
"Add User": "Add User",
|
"Add User": "Add User",
|
||||||
|
"Home": "Home",
|
||||||
"Enter a server name": "Enter a server name",
|
"Enter a server name": "Enter a server name",
|
||||||
"Looks good": "Looks good",
|
"Looks good": "Looks good",
|
||||||
"Can't find this server or its room list": "Can't find this server or its room list",
|
"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|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|zero": "Uploading %(filename)s",
|
||||||
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
|
"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 light mode": "Switch to light mode",
|
||||||
"Switch to dark mode": "Switch to dark mode",
|
"Switch to dark mode": "Switch to dark mode",
|
||||||
"Switch theme": "Switch theme",
|
"Switch theme": "Switch theme",
|
||||||
|
|
|
@ -166,6 +166,25 @@ class TagOrderStore extends Store {
|
||||||
selectedTags: newTags,
|
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');
|
Analytics.trackEvent('FilterStore', 'select_tag');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue