Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
Browse files Browse the repository at this point in the history
… t3chguy/fix/18858
  • Loading branch information
t3chguy committed Sep 16, 2021
2 parents b8ee2d2 + edf7764 commit b052d07
Show file tree
Hide file tree
Showing 24 changed files with 827 additions and 777 deletions.
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@
"prop-types": "^15.7.2",
"qrcode": "^1.4.4",
"re-resizable": "^6.9.0",
"react": "^17.0.2",
"react": "17.0.2",
"react-beautiful-dnd": "^13.1.0",
"react-blurhash": "^0.1.3",
"react-dom": "^17.0.2",
"react-dom": "17.0.2",
"react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1",
"resize-observer-polyfill": "^1.5.1",
Expand Down Expand Up @@ -142,9 +142,9 @@
"@types/pako": "^1.0.1",
"@types/parse5": "^6.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "^17.0.2",
"@types/react": "17.0.14",
"@types/react-beautiful-dnd": "^13.0.0",
"@types/react-dom": "^17.0.2",
"@types/react-dom": "17.0.9",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "^2.3.1",
"@types/zxcvbn": "^4.4.0",
Expand Down Expand Up @@ -175,9 +175,12 @@
"stylelint": "^13.9.0",
"stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0",
"typescript": "^4.1.3",
"typescript": "4.3.5",
"walk": "^2.3.14"
},
"resolutions": {
"@types/react": "17.0.14"
},
"jest": {
"testEnvironment": "./__test-utils__/environment.js",
"testMatch": [
Expand Down
26 changes: 14 additions & 12 deletions res/css/views/rooms/_EventBubbleTile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ limitations under the License.
}

.mx_EventTile[data-layout=bubble] {

position: relative;
margin-top: var(--gutterSize);
margin-left: 50px;
margin-left: 49px;
margin-right: 100px;
font-size: $font-14px;

&.mx_EventTile_continuation {
margin-top: 2px;
Expand Down Expand Up @@ -77,10 +77,11 @@ limitations under the License.
max-width: 70%;
}

.mx_SenderProfile {
> .mx_SenderProfile {
position: relative;
top: -2px;
left: 2px;
font-size: $font-15px;
}

&[data-self=false] {
Expand Down Expand Up @@ -113,8 +114,6 @@ limitations under the License.

.mx_ReplyTile .mx_SenderProfile {
display: block;
top: unset;
left: unset;
}

.mx_ReactionsRow {
Expand Down Expand Up @@ -287,16 +286,19 @@ limitations under the License.
.mx_EventTile_line,
.mx_EventTile_info {
min-width: 100%;
// Preserve alignment with left edge of text in bubbles
margin: 0;
}

.mx_EventTile_e2eIcon {
margin-left: 9px;
}

.mx_EventTile_line > a {
// Align timestamps with those of normal bubble tiles
right: auto;
top: -15px;
left: -68px;
top: -11px;
left: -95px;
}
}

Expand Down Expand Up @@ -326,11 +328,10 @@ limitations under the License.
}

.mx_EventTile_line {
margin: 0 5px;
margin: 0;
> a {
left: auto;
right: 0;
transform: translateX(calc(100% + 5px));
// Align timestamps with those of normal bubble tiles
left: -76px;
}
}

Expand All @@ -340,7 +341,8 @@ limitations under the License.
}

.mx_EventListSummary[data-expanded=false][data-layout=bubble] {
padding: 0 34px;
// Align with left edge of bubble tiles
padding: 0 49px;
}

/* events that do not require bubble layout */
Expand Down
14 changes: 6 additions & 8 deletions res/css/views/rooms/_RoomSublist.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,12 @@ limitations under the License.
}
}

// In the general case, we leave height of headers alone even if sticky, so
// that the sublists below them do not jump. However, that leaves a gap
// when scrolled to the top above the first sublist (whose header can only
// ever stick to top), so we force height to 0 for only that first header.
// See also https://github.com/vector-im/element-web/issues/14429.
&:first-child .mx_RoomSublist_headerContainer {
height: 0;
padding-bottom: 4px;
// In the general case, we reserve space for each sublist header to prevent
// scroll jumps when they become sticky. However, that leaves a gap when
// scrolled to the top above the first sublist (whose header can only ever
// stick to top), so we make sure to exclude the first visible sublist.
&:not(.mx_RoomSublist_hidden) ~ .mx_RoomSublist .mx_RoomSublist_headerContainer {
height: 24px;
}

.mx_RoomSublist_resizeBox {
Expand Down
5 changes: 3 additions & 2 deletions src/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,11 +574,12 @@ async function doSetLoggedIn(
await abortLogin();
}

PosthogAnalytics.instance.updateAnonymityFromSettings(credentials.userId);

Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl);

MatrixClientPeg.replaceUsingCreds(credentials);

PosthogAnalytics.instance.updateAnonymityFromSettings(credentials.userId);

const client = MatrixClientPeg.get();

if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) {
Expand Down
10 changes: 5 additions & 5 deletions src/MediaDeviceHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ limitations under the License.

import SettingsStore from "./settings/SettingsStore";
import { SettingLevel } from "./settings/SettingLevel";
import { setMatrixCallAudioInput, setMatrixCallVideoInput } from "matrix-js-sdk/src/matrix";
import EventEmitter from 'events';
import { MatrixClientPeg } from "./MatrixClientPeg";

// XXX: MediaDeviceKind is a union type, so we make our own enum
export enum MediaDeviceKindEnum {
Expand Down Expand Up @@ -74,8 +74,8 @@ export default class MediaDeviceHandler extends EventEmitter {
const audioDeviceId = SettingsStore.getValue("webrtc_audioinput");
const videoDeviceId = SettingsStore.getValue("webrtc_videoinput");

setMatrixCallAudioInput(audioDeviceId);
setMatrixCallVideoInput(videoDeviceId);
MatrixClientPeg.get().getMediaHandler().setAudioInput(audioDeviceId);
MatrixClientPeg.get().getMediaHandler().setVideoInput(videoDeviceId);
}

public setAudioOutput(deviceId: string): void {
Expand All @@ -90,7 +90,7 @@ export default class MediaDeviceHandler extends EventEmitter {
*/
public setAudioInput(deviceId: string): void {
SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId);
setMatrixCallAudioInput(deviceId);
MatrixClientPeg.get().getMediaHandler().setAudioInput(deviceId);
}

/**
Expand All @@ -100,7 +100,7 @@ export default class MediaDeviceHandler extends EventEmitter {
*/
public setVideoInput(deviceId: string): void {
SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId);
setMatrixCallVideoInput(deviceId);
MatrixClientPeg.get().getMediaHandler().setVideoInput(deviceId);
}

public setDevice(deviceId: string, kind: MediaDeviceKindEnum): void {
Expand Down
76 changes: 40 additions & 36 deletions src/PosthogAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import posthog, { PostHog } from 'posthog-js';
import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig';
import SettingsStore from './settings/SettingsStore';
import { MatrixClientPeg } from "./MatrixClientPeg";
import { MatrixClient } from "matrix-js-sdk/src/client";

/* Posthog analytics tracking.
*
Expand All @@ -27,10 +29,11 @@ import SettingsStore from './settings/SettingsStore';
* - If [Do Not Track](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/doNotTrack) is
* enabled, events are not sent (this detection is built into posthog and turned on via the
* `respect_dnt` flag being passed to `posthog.init`).
* - If the `feature_pseudonymous_analytics_opt_in` labs flag is `true`, track pseudonomously, i.e.
* hash all matrix identifiers in tracking events (user IDs, room IDs etc) using SHA-256.
* - Otherwise, if the existing `analyticsOptIn` flag is `true`, track anonymously, i.e.
* redact all matrix identifiers in tracking events.
* - If the `feature_pseudonymous_analytics_opt_in` labs flag is `true`, track pseudonomously by maintaining
* a randomised analytics ID in account_data for that user (shared between devices) and sending it to posthog to
identify the user.
* - Otherwise, if the existing `analyticsOptIn` flag is `true`, track anonymously, i.e. do not identify the user
using any identifier that would be consistent across devices.
* - If both flags are false or not set, events are not sent.
*/

Expand Down Expand Up @@ -71,12 +74,6 @@ interface IPageView extends IAnonymousEvent {
};
}

const hashHex = async (input: string): Promise<string> => {
const buf = new TextEncoder().encode(input);
const digestBuf = await window.crypto.subtle.digest("sha-256", buf);
return [...new Uint8Array(digestBuf)].map((b: number) => b.toString(16).padStart(2, "0")).join("");
};

const whitelistedScreens = new Set([
"register", "login", "forgot_password", "soft_logout", "new", "settings", "welcome", "home", "start", "directory",
"start_sso", "start_cas", "groups", "complete_security", "post_registration", "room", "user", "group",
Expand All @@ -89,7 +86,6 @@ export async function getRedactedCurrentLocation(
anonymity: Anonymity,
): Promise<string> {
// Redact PII from the current location.
// If anonymous is true, redact entirely, if false, substitute it with a hash.
// For known screens, assumes a URL structure of /<screen name>/might/be/pii
if (origin.startsWith('file://')) {
pathname = "/<redacted_file_scheme_url>/";
Expand All @@ -99,17 +95,13 @@ export async function getRedactedCurrentLocation(
if (hash == "") {
hashStr = "";
} else {
let [beforeFirstSlash, screen, ...parts] = hash.split("/");
let [beforeFirstSlash, screen] = hash.split("/");

if (!whitelistedScreens.has(screen)) {
screen = "<redacted_screen_name>";
}

for (let i = 0; i < parts.length; i++) {
parts[i] = anonymity === Anonymity.Anonymous ? `<redacted>` : await hashHex(parts[i]);
}

hashStr = `${beforeFirstSlash}/${screen}/${parts.join("/")}`;
hashStr = `${beforeFirstSlash}/${screen}/<redacted>`;
}
return origin + pathname + hashStr;
}
Expand All @@ -123,15 +115,15 @@ export class PosthogAnalytics {
/* Wrapper for Posthog analytics.
* 3 modes of anonymity are supported, governed by this.anonymity
* - Anonymity.Disabled means *no data* is passed to posthog
* - Anonymity.Anonymous means all identifers will be redacted before being passed to posthog
* - Anonymity.Pseudonymous means all identifiers will be hashed via SHA-256 before being passed
* to Posthog
* - Anonymity.Anonymous means no identifier is passed to posthog
* - Anonymity.Pseudonymous means an analytics ID stored in account_data and shared between devices
* is passed to posthog.
*
* To update anonymity, call updateAnonymityFromSettings() or you can set it directly via setAnonymity().
*
* To pass an event to Posthog:
*
* 1. Declare a type for the event, extending IAnonymousEvent, IPseudonymousEvent or IRoomEvent.
* 1. Declare a type for the event, extending IAnonymousEvent or IPseudonymousEvent.
* 2. Call the appropriate track*() method. Pseudonymous events will be dropped when anonymity is
* Anonymous or Disabled; Anonymous events will be dropped when anonymity is Disabled.
*/
Expand All @@ -141,6 +133,7 @@ export class PosthogAnalytics {
private enabled = false;
private static _instance = null;
private platformSuperProperties = {};
private static ANALYTICS_ID_EVENT_TYPE = "im.vector.web.analytics_id";

public static get instance(): PosthogAnalytics {
if (!this._instance) {
Expand Down Expand Up @@ -274,9 +267,32 @@ export class PosthogAnalytics {
this.anonymity = anonymity;
}

public async identifyUser(userId: string): Promise<void> {
private static getRandomAnalyticsId(): string {
return [...crypto.getRandomValues(new Uint8Array(16))].map((c) => c.toString(16)).join('');
}

public async identifyUser(client: MatrixClient, analyticsIdGenerator: () => string): Promise<void> {
if (this.anonymity == Anonymity.Pseudonymous) {
this.posthog.identify(await hashHex(userId));
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
// different devices to send the same ID.
try {
const accountData = await client.getAccountDataFromServer(PosthogAnalytics.ANALYTICS_ID_EVENT_TYPE);
let analyticsID = accountData?.id;
if (!analyticsID) {
// Couldn't retrieve an analytics ID from user settings, so create one and set it on the server.
// Note there's a race condition here - if two devices do these steps at the same time, last write
// wins, and the first writer will send tracking with an ID that doesn't match the one on the server
// until the next time account data is refreshed and this function is called (most likely on next
// page load). This will happen pretty infrequently, so we can tolerate the possibility.
analyticsID = analyticsIdGenerator();
await client.setAccountData("im.vector.web.analytics_id", { id: analyticsID });
}
this.posthog.identify(analyticsID);
} catch (e) {
// The above could fail due to network requests, but not essential to starting the application,
// so swallow it.
console.log("Unable to identify user for tracking" + e.toString());
}
}
}

Expand Down Expand Up @@ -307,18 +323,6 @@ export class PosthogAnalytics {
await this.capture(eventName, properties);
}

public async trackRoomEvent<E extends IRoomEvent>(
eventName: E["eventName"],
roomId: string,
properties: Omit<E["properties"], "roomId">,
): Promise<void> {
const updatedProperties = {
...properties,
hashedRoomId: roomId ? await hashHex(roomId) : null,
};
await this.trackPseudonymousEvent(eventName, updatedProperties);
}

public async trackPageView(durationMs: number): Promise<void> {
const hash = window.location.hash;

Expand Down Expand Up @@ -349,7 +353,7 @@ export class PosthogAnalytics {
// Identify the user (via hashed user ID) to posthog if anonymity is pseudonmyous
this.setAnonymity(PosthogAnalytics.getAnonymityFromSettings());
if (userId && this.getAnonymity() == Anonymity.Pseudonymous) {
await this.identifyUser(userId);
await this.identifyUser(MatrixClientPeg.get(), PosthogAnalytics.getRandomAnalyticsId);
}
}
}
5 changes: 0 additions & 5 deletions src/Resend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ export default class Resend {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/element-web/issues/3148
console.log('Resend got send failure: ' + err.name + '(' + err + ')');

dis.dispatch({
action: 'message_send_failed',
event: event,
});
});
}

Expand Down
7 changes: 1 addition & 6 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1897,15 +1897,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {

onSendEvent(roomId: string, event: MatrixEvent) {
const cli = MatrixClientPeg.get();
if (!cli) {
dis.dispatch({ action: 'message_send_failed' });
return;
}
if (!cli) return;

cli.sendEvent(roomId, event.getType(), event.getContent()).then(() => {
dis.dispatch({ action: 'message_sent' });
}, (err) => {
dis.dispatch({ action: 'message_send_failed' });
});
}

Expand Down
Loading

0 comments on commit b052d07

Please sign in to comment.