Merge branch 'develop' into travis/breadcrumbs/telemetry

This commit is contained in:
Travis Ralston 2019-04-05 09:35:38 -06:00
commit e2edae3383
8 changed files with 168 additions and 39 deletions

View file

@ -19,10 +19,15 @@ limitations under the License.
height: 42px;
padding: 8px;
padding-bottom: 0;
overflow-x: visible;
display: flex;
flex-direction: row;
// Autohide the scrollbar
overflow-x: hidden;
&:hover {
overflow-x: visible;
}
.mx_AutoHideScrollbar_offset {
display: flex;
flex-direction: row;
@ -41,6 +46,12 @@ limitations under the License.
top: -3px;
right: -4px;
}
.mx_RoomBreadcrumbs_dmIndicator {
position: absolute;
bottom: 0;
right: -4px;
}
}
.mx_RoomBreadcrumbs_animate {

View file

@ -40,6 +40,13 @@ export default class IndicatorScrollbar extends React.Component {
};
}
moveToOrigin() {
if (!this._scrollElement) return;
this._scrollElement.scrollLeft = 0;
this._scrollElement.scrollTop = 0;
}
_collectScroller(scroller) {
if (scroller && !this._scrollElement) {
this._scrollElement = scroller;

View file

@ -536,7 +536,9 @@ module.exports = React.createClass({
payload.data.description || payload.data.name);
break;
case 'picture_snapshot':
this.uploadFile(payload.file);
return ContentMessages.sharedInstance().sendContentListToRoom(
[payload.file], this.state.room.roomId, MatrixClientPeg.get(),
);
break;
case 'notifier_enabled':
case 'upload_started':

View file

@ -47,7 +47,7 @@ import {Completion} from "../../../autocomplete/Autocompleter";
import Markdown from '../../../Markdown';
import ComposerHistoryManager from '../../../ComposerHistoryManager';
import MessageComposerStore from '../../../stores/MessageComposerStore';
import ContentMessage from '../../../ContentMessages';
import ContentMessages from '../../../ContentMessages';
import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix';
@ -139,8 +139,6 @@ export default class MessageComposerInput extends React.Component {
// js-sdk Room object
room: PropTypes.object.isRequired,
onFilesPasted: PropTypes.func,
onInputStateChanged: PropTypes.func,
};
@ -1014,7 +1012,7 @@ export default class MessageComposerInput extends React.Component {
// neither chrome nor firefox let you paste a plain file copied
// from Finder) but more images copied from a different website
// / word processor etc.
return ContentMessage.sharedInstance().sendContentListToRoom(
return ContentMessages.sharedInstance().sendContentListToRoom(
transfer.files, this.props.room.roomId, this.client,
);
case 'html': {

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
import React from "react";
import dis from "../../../dispatcher";
import MatrixClientPeg from "../../../MatrixClientPeg";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import AccessibleButton from '../elements/AccessibleButton';
import RoomAvatar from '../avatars/RoomAvatar';
import classNames from 'classnames';
@ -25,6 +25,8 @@ import sdk from "../../../index";
import Analytics from "../../../Analytics";
import * as RoomNotifs from '../../../RoomNotifs';
import * as FormattingUtils from "../../../utils/FormattingUtils";
import DMRoomMap from "../../../utils/DMRoomMap";
import {_t} from "../../../languageHandler";
const MAX_ROOMS = 20;
@ -39,22 +41,22 @@ export default class RoomBreadcrumbs extends React.Component {
componentWillMount() {
this._dispatcherRef = dis.register(this.onAction);
const roomStr = localStorage.getItem("mx_breadcrumb_rooms");
if (roomStr) {
try {
const roomIds = JSON.parse(roomStr);
this.setState({
rooms: roomIds.map((r) => {
return {
room: MatrixClientPeg.get().getRoom(r),
animated: false,
};
}).filter((r) => r.room),
});
} catch (e) {
console.error("Failed to parse breadcrumbs:", e);
let storedRooms = SettingsStore.getValue("breadcrumb_rooms");
if (!storedRooms || !storedRooms.length) {
// Fallback to the rooms stored in localstorage for those who would have had this.
// TODO: Remove this after a bit - the feature was only on develop, so a few weeks should be plenty time.
const roomStr = localStorage.getItem("mx_breadcrumb_rooms");
if (roomStr) {
try {
storedRooms = JSON.parse(roomStr);
} catch (e) {
console.error("Failed to parse breadcrumbs:", e);
}
}
}
this._loadRoomIds(storedRooms || []);
this._settingWatchRef = SettingsStore.watchSetting("breadcrumb_rooms", null, this.onBreadcrumbsChanged);
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt);
@ -65,6 +67,8 @@ export default class RoomBreadcrumbs extends React.Component {
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
SettingsStore.unwatchSetting(this._settingWatchRef);
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("Room.myMembership", this.onMyMembership);
@ -78,15 +82,17 @@ export default class RoomBreadcrumbs extends React.Component {
const rooms = this.state.rooms.slice();
if (rooms.length) {
const {room, animated} = rooms[0];
if (!animated) {
rooms[0] = {room, animated: true};
const roomModel = rooms[0];
if (!roomModel.animated) {
roomModel.animated = true;
setTimeout(() => this.setState({rooms}), 0);
}
}
const roomStr = JSON.stringify(rooms.map((r) => r.room.roomId));
localStorage.setItem("mx_breadcrumb_rooms", roomStr);
const roomIds = rooms.map((r) => r.room.roomId);
if (roomIds.length > 0) {
SettingsStore.setValue("breadcrumb_rooms", null, SettingLevel.ACCOUNT, roomIds);
}
}
onAction(payload) {
@ -126,17 +132,50 @@ export default class RoomBreadcrumbs extends React.Component {
}
};
_calculateRoomBadges(room) {
if (!room) return;
onBreadcrumbsChanged = (settingName, roomId, level, valueAtLevel, value) => {
if (!value) return;
const rooms = this.state.rooms.slice();
const roomModel = rooms.find((r) => r.room.roomId === room.roomId);
if (!roomModel) return; // No applicable room, so don't do math on it
const currentState = this.state.rooms.map((r) => r.room.roomId);
if (currentState.length === value.length) {
let changed = false;
for (let i = 0; i < currentState.length; i++) {
if (currentState[i] !== value[i]) {
changed = true;
break;
}
}
if (!changed) return;
}
this._loadRoomIds(value);
};
_loadRoomIds(roomIds) {
if (!roomIds || roomIds.length <= 0) return; // Skip updates with no rooms
// If we're here, the list changed.
const rooms = roomIds.map((r) => MatrixClientPeg.get().getRoom(r)).filter((r) => r).map((r) => {
const badges = this._calculateBadgesForRoom(r) || {};
return {
room: r,
animated: false,
...badges,
};
});
this.setState({
rooms: rooms,
});
}
_calculateBadgesForRoom(room) {
if (!room) return null;
// Reset the notification variables for simplicity
roomModel.redBadge = false;
roomModel.formattedCount = "0";
roomModel.showCount = false;
const roomModel = {
redBadge: false,
formattedCount: "0",
showCount: false,
};
const notifState = RoomNotifs.getRoomNotifsState(room.roomId);
if (RoomNotifs.MENTION_BADGE_STATES.includes(notifState)) {
@ -156,24 +195,57 @@ export default class RoomBreadcrumbs extends React.Component {
}
}
return roomModel;
}
_calculateRoomBadges(room) {
if (!room) return;
const rooms = this.state.rooms.slice();
const roomModel = rooms.find((r) => r.room.roomId === room.roomId);
if (!roomModel) return; // No applicable room, so don't do math on it
const badges = this._calculateBadgesForRoom(room);
if (!badges) return; // No badges for some reason
Object.assign(roomModel, badges);
this.setState({rooms});
}
_appendRoomId(roomId) {
const room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
return;
}
let room = MatrixClientPeg.get().getRoom(roomId);
if (!room) return;
const rooms = this.state.rooms.slice();
// If the room is upgraded, use that room instead. We'll also splice out
// any children of the room.
const history = MatrixClientPeg.get().getRoomUpgradeHistory(roomId);
if (history.length > 1) {
room = history[history.length - 1]; // Last room is most recent
// Take out any room that isn't the most recent room
for (let i = 0; i < history.length - 1; i++) {
const idx = rooms.findIndex((r) => r.room.roomId === history[i].roomId);
if (idx !== -1) rooms.splice(idx, 1);
}
}
const existingIdx = rooms.findIndex((r) => r.room.roomId === room.roomId);
if (existingIdx !== -1) {
rooms.splice(existingIdx, 1);
}
rooms.splice(0, 0, {room, animated: false});
if (rooms.length > MAX_ROOMS) {
rooms.splice(MAX_ROOMS, rooms.length - MAX_ROOMS);
}
this.setState({rooms});
if (this.refs.scroller) {
this.refs.scroller.moveToOrigin();
}
}
_viewRoom(room, index) {
@ -197,6 +269,11 @@ export default class RoomBreadcrumbs extends React.Component {
this.setState({rooms});
}
_isDmRoom(room) {
const dmRooms = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
return Boolean(dmRooms);
}
render() {
const Tooltip = sdk.getComponent('elements.Tooltip');
const IndicatorScrollbar = sdk.getComponent('structures.IndicatorScrollbar');
@ -234,11 +311,23 @@ export default class RoomBreadcrumbs extends React.Component {
badge = <div className={badgeClasses}>{r.formattedCount}</div>;
}
let dmIndicator;
if (this._isDmRoom(r.room)) {
dmIndicator = <img
src={require("../../../../res/img/icon_person.svg")}
className="mx_RoomBreadcrumbs_dmIndicator"
width="13"
height="15"
alt={_t("Direct Chat")}
/>;
}
return (
<AccessibleButton className={classes} key={r.room.roomId} onClick={() => this._viewRoom(r.room, i)}
onMouseEnter={() => this._onMouseEnter(r.room)} onMouseLeave={() => this._onMouseLeave(r.room)}>
<RoomAvatar room={r.room} width={32} height={32} />
{badge}
{dmIndicator}
{tooltip}
</AccessibleButton>
);

View file

@ -761,6 +761,7 @@
"Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s",
"Seen by %(displayName)s (%(userName)s) at %(dateTime)s": "Seen by %(displayName)s (%(userName)s) at %(dateTime)s",
"Replying": "Replying",
"Direct Chat": "Direct Chat",
"No rooms to show": "No rooms to show",
"Unnamed room": "Unnamed room",
"World readable": "World readable",
@ -1264,7 +1265,6 @@
"Forget": "Forget",
"Favourite": "Favourite",
"Low Priority": "Low Priority",
"Direct Chat": "Direct Chat",
"Clear status": "Clear status",
"Update status": "Update status",
"Set status": "Set status",

View file

@ -258,6 +258,10 @@ export const SETTINGS = {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
default: "en",
},
"breadcrumb_rooms": {
supportedLevels: ['account'],
default: [],
},
"analyticsOptIn": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
displayName: _td('Send analytics data'),

View file

@ -19,6 +19,8 @@ import MatrixClientPeg from '../../MatrixClientPeg';
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
import {SettingLevel} from "../SettingsStore";
const BREADCRUMBS_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms";
/**
* Gets and sets settings at the "account" level for the current user.
* This handler does not make use of the roomId parameter.
@ -55,6 +57,9 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
const val = event.getContent()[settingName];
this._watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val);
}
} else if (event.getType() === BREADCRUMBS_EVENT_TYPE) {
const val = event.getContent()['rooms'] || [];
this._watchers.notifyUpdate("breadcrumb_rooms", null, SettingLevel.ACCOUNT, val);
}
}
@ -68,6 +73,12 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
return !content['disable'];
}
// Special case for breadcrumbs
if (settingName === "breadcrumb_rooms") {
const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {};
return content['rooms'] || [];
}
const settings = this._getSettings() || {};
let preferredValue = settings[settingName];
@ -89,6 +100,13 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
return MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", content);
}
// Special case for breadcrumbs
if (settingName === "breadcrumb_rooms") {
const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {};
content['rooms'] = newValue;
return MatrixClientPeg.get().setAccountData(BREADCRUMBS_EVENT_TYPE, content);
}
const content = this._getSettings() || {};
content[settingName] = newValue;
return MatrixClientPeg.get().setAccountData("im.vector.web.settings", content);