diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss index 23cecff477..7a5d69fc8a 100644 --- a/res/css/views/rooms/_RoomTile2.scss +++ b/res/css/views/rooms/_RoomTile2.scss @@ -81,7 +81,7 @@ limitations under the License. } } - .mx_RoomTile2_menuButton { + .mx_RoomTile2_notificationsButton { margin-left: 4px; // spacing between buttons } @@ -112,7 +112,8 @@ limitations under the License. width: 20px; min-width: 20px; // yay flex height: 20px; - margin: auto 0; + margin-top: auto; + margin-bottom: auto; position: relative; display: none; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index c4b4262642..8469a85bfe 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -36,7 +36,7 @@ $focus-bg-color: #dddddd; $accent-fg-color: #ffffff; $accent-color-50pct: rgba(3, 179, 129, 0.5); //#03b381 in rgb $accent-color-darker: #92caad; -$accent-color-alt: #238CF5; +$accent-color-alt: #238cf5; $selection-fg-color: $primary-bg-color; @@ -46,8 +46,8 @@ $focus-brightness: 105%; $warning-color: $notice-primary-color; // red $orange-warning-color: #ff8d13; // used for true warnings // background colour for warnings -$warning-bg-color: #DF2A8B; -$info-bg-color: #2A9EDF; +$warning-bg-color: #df2a8b; +$info-bg-color: #2a9edf; $mention-user-pill-bg-color: $warning-color; $other-user-pill-bg-color: rgba(0, 0, 0, 0.1); @@ -71,7 +71,7 @@ $tagpanel-bg-color: #27303a; $plinth-bg-color: $secondary-accent-color; // used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.5); +$droptarget-bg-color: rgba(255, 255, 255, 0.5); // used by AddressSelector $selected-color: $secondary-accent-color; @@ -157,18 +157,18 @@ $rte-group-pill-color: #aaa; $topleftmenu-color: #212121; $roomheader-color: #45474a; -$roomheader-addroom-bg-color: #91A1C0; +$roomheader-addroom-bg-color: #91a1c0; $roomheader-addroom-fg-color: $accent-fg-color; -$tagpanel-button-color: #91A1C0; -$roomheader-button-color: #91A1C0; -$groupheader-button-color: #91A1C0; -$rightpanel-button-color: #91A1C0; -$composer-button-color: #91A1C0; +$tagpanel-button-color: #91a1c0; +$roomheader-button-color: #91a1c0; +$groupheader-button-color: #91a1c0; +$rightpanel-button-color: #91a1c0; +$composer-button-color: #91a1c0; $roomtopic-color: #9e9e9e; $eventtile-meta-color: $roomtopic-color; $composer-e2e-icon-color: #c9ced6; -$header-divider-color: #91A1C0; +$header-divider-color: #91a1c0; // ******************** @@ -184,11 +184,11 @@ $roomsublist2-divider-color: $primary-fg-color; $roomtile2-preview-color: #9e9e9e; $roomtile2-default-badge-bg-color: #61708b; -$roomtile2-selected-bg-color: #FFF; +$roomtile2-selected-bg-color: #fff; $presence-online: $accent-color; -$presence-away: orange; // TODO: Get color -$presence-offline: #E3E8F0; +$presence-away: #d9b072; +$presence-offline: #e3e8f0; // ******************** diff --git a/src/RoomNotifsTypes.ts b/src/RoomNotifsTypes.ts new file mode 100644 index 0000000000..0e7093e434 --- /dev/null +++ b/src/RoomNotifsTypes.ts @@ -0,0 +1,24 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { + ALL_MESSAGES, + ALL_MESSAGES_LOUD, + MENTIONS_ONLY, + MUTE, +} from "./RoomNotifs"; + +export type Volume = ALL_MESSAGES_LOUD | ALL_MESSAGES | MENTIONS_ONLY | MUTE; diff --git a/src/components/views/rooms/RoomList2.tsx b/src/components/views/rooms/RoomList2.tsx index 8adf53269b..fb0136fb29 100644 --- a/src/components/views/rooms/RoomList2.tsx +++ b/src/components/views/rooms/RoomList2.tsx @@ -70,8 +70,6 @@ interface IState { } const TAG_ORDER: TagID[] = [ - // -- Community Invites Placeholder -- - DefaultTagID.Invite, DefaultTagID.Favourite, DefaultTagID.DM, @@ -83,7 +81,6 @@ const TAG_ORDER: TagID[] = [ DefaultTagID.ServerNotice, DefaultTagID.Archived, ]; -const COMMUNITY_TAGS_BEFORE_TAG = DefaultTagID.Invite; const CUSTOM_TAGS_BEFORE_TAG = DefaultTagID.LowPriority; const ALWAYS_VISIBLE_TAGS: TagID[] = [ DefaultTagID.DM, @@ -278,17 +275,15 @@ export default class RoomList2 extends React.Component { const components: React.ReactElement[] = []; for (const orderedTagId of TAG_ORDER) { - if (COMMUNITY_TAGS_BEFORE_TAG === orderedTagId) { - // Populate community invites if we have the chance - // TODO: Community invites: https://github.com/vector-im/riot-web/issues/14179 - } if (CUSTOM_TAGS_BEFORE_TAG === orderedTagId) { // Populate custom tags if needed // TODO: Custom tags: https://github.com/vector-im/riot-web/issues/14091 } const orderedRooms = this.state.sublists[orderedTagId] || []; - if (orderedRooms.length === 0 && !ALWAYS_VISIBLE_TAGS.includes(orderedTagId)) { + const extraTiles = orderedTagId === DefaultTagID.Invite ? this.renderCommunityInvites() : null; + const totalTiles = orderedRooms.length + (extraTiles ? extraTiles.length : 0); + if (totalTiles === 0 && !ALWAYS_VISIBLE_TAGS.includes(orderedTagId)) { continue; // skip tag - not needed } @@ -296,7 +291,6 @@ export default class RoomList2 extends React.Component { if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`); const onAddRoomFn = aesthetics.onAddRoom ? () => aesthetics.onAddRoom(dis) : null; - const extraTiles = orderedTagId === DefaultTagID.Invite ? this.renderCommunityInvites() : null; components.push( { const tiles: React.ReactElement[] = []; - if (this.props.extraBadTilesThatShouldntExist) { - tiles.push(...this.props.extraBadTilesThatShouldntExist); - } - if (this.props.rooms) { const visibleRooms = this.props.rooms.slice(0, this.numVisibleTiles); for (const room of visibleRooms) { @@ -329,6 +326,10 @@ export default class RoomSublist2 extends React.Component { } } + if (this.props.extraBadTilesThatShouldntExist) { + tiles.push(...this.props.extraBadTilesThatShouldntExist); + } + // We only have to do this because of the extra tiles. We do it conditionally // to avoid spending cycles on slicing. It's generally fine to do this though // as users are unlikely to have more than a handful of tiles when the extra @@ -341,15 +342,42 @@ export default class RoomSublist2 extends React.Component { } private renderMenu(): React.ReactElement { - // TODO: Get a proper invite context menu, or take invites out of the room list. - if (this.props.tagId === DefaultTagID.Invite) { - return null; - } - let contextMenu = null; if (this.state.contextMenuPosition) { const isAlphabetical = RoomListStore.instance.getTagSorting(this.props.tagId) === SortAlgorithm.Alphabetic; const isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance; + + // Invites don't get some nonsense options, so only add them if we have to. + let otherSections = null; + if (this.props.tagId !== DefaultTagID.Invite) { + otherSections = ( + +
+
+
{_t("Unread rooms")}
+ + {_t("Always show first")} + +
+
+
+
{_t("Show")}
+ + {_t("Message preview")} + +
+
+ ); + } + contextMenu = ( { {_t("A-Z")} -
-
-
{_t("Unread rooms")}
- - {_t("Always show first")} - -
-
-
-
{_t("Show")}
- - {_t("Message preview")} - -
+ {otherSections}
); diff --git a/src/components/views/rooms/RoomTile2.tsx b/src/components/views/rooms/RoomTile2.tsx index 21901026a5..ea5b888e2c 100644 --- a/src/components/views/rooms/RoomTile2.tsx +++ b/src/components/views/rooms/RoomTile2.tsx @@ -49,6 +49,7 @@ import { TagSpecificNotificationState } from "../../../stores/notifications/TagS import { INotificationState } from "../../../stores/notifications/INotificationState"; import NotificationBadge from "./NotificationBadge"; import { NotificationColor } from "../../../stores/notifications/NotificationColor"; +import { Volume } from "../../../RoomNotifsTypes"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import {ActionPayload} from "../../../dispatcher/payloads"; @@ -82,8 +83,6 @@ interface IState { generalMenuPosition: PartialDOMRect; } -type Volume = ALL_MESSAGES_LOUD | ALL_MESSAGES | MENTIONS_ONLY | MUTE; - const messagePreviewId = (roomId: string) => `mx_RoomTile2_messagePreview_${roomId}`; const contextMenuBelow = (elementRect: PartialDOMRect) => { @@ -529,8 +528,8 @@ export default class RoomTile2 extends React.Component { {roomAvatar} {nameContainer} {badge} - {this.renderNotificationsMenu(isActive)} {this.renderGeneralMenu()} + {this.renderNotificationsMenu(isActive)} } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f9a2f3a5a0..f16fb38f86 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1200,13 +1200,13 @@ "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", "Not now": "Not now", "Don't ask me again": "Don't ask me again", - "Sort by": "Sort by", - "Activity": "Activity", - "A-Z": "A-Z", "Unread rooms": "Unread rooms", "Always show first": "Always show first", "Show": "Show", "Message preview": "Message preview", + "Sort by": "Sort by", + "Activity": "Activity", + "A-Z": "A-Z", "List options": "List options", "Jump to first unread room.": "Jump to first unread room.", "Jump to first invite.": "Jump to first invite.", diff --git a/src/stores/room-list/ListLayout.ts b/src/stores/room-list/ListLayout.ts index efb0c4bdfb..f31e92b8ae 100644 --- a/src/stores/room-list/ListLayout.ts +++ b/src/stores/room-list/ListLayout.ts @@ -85,8 +85,8 @@ export class ListLayout { } public get defaultVisibleTiles(): number { - // 10 is what "feels right", and mostly subject to design's opinion. - return 10 + RESIZER_BOX_FACTOR; + // This number is what "feels right", and mostly subject to design's opinion. + return 5 + RESIZER_BOX_FACTOR; } public setVisibleTilesWithin(diff: number, maxPossible: number) { diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index a122ee3ae6..e7ca94ed95 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -19,6 +19,7 @@ import { TagID } from "../../models"; import { IAlgorithm } from "./IAlgorithm"; import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import * as Unread from "../../../../Unread"; +import { EffectiveMembership, getEffectiveMembership } from "../../membership"; /** * Sorts rooms according to the last event's timestamp in each room that seems @@ -37,6 +38,8 @@ export class RecentAlgorithm implements IAlgorithm { // actually changed (probably needs to be done higher up?) then we could do an // insertion sort or similar on the limited set of changes. + const myUserId = MatrixClientPeg.get().getUserId(); + const tsCache: { [roomId: string]: number } = {}; const getLastTs = (r: Room) => { if (tsCache[r.roomId]) { @@ -50,13 +53,23 @@ export class RecentAlgorithm implements IAlgorithm { return Number.MAX_SAFE_INTEGER; } + // If the room hasn't been joined yet, it probably won't have a timeline to + // parse. We'll still fall back to the timeline if this fails, but chances + // are we'll at least have our own membership event to go off of. + const effectiveMembership = getEffectiveMembership(r.getMyMembership()); + if (effectiveMembership !== EffectiveMembership.Join) { + const membershipEvent = r.currentState.getStateEvents("m.room.member", myUserId); + if (membershipEvent && !Array.isArray(membershipEvent)) { + return membershipEvent.getTs(); + } + } + for (let i = r.timeline.length - 1; i >= 0; --i) { const ev = r.timeline[i]; if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) // TODO: Don't assume we're using the same client as the peg - if (ev.getSender() === MatrixClientPeg.get().getUserId() - || Unread.eventTriggersUnreadCount(ev)) { + if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) { return ev.getTs(); } }