Merge branch 'experimental' into bwindels/memberqueryfield
commit
883126a528
|
@ -199,25 +199,12 @@ module.exports = function (config) {
|
|||
|
||||
'matrix-react-sdk': path.resolve('test/skinned-sdk.js'),
|
||||
'sinon': 'sinon/pkg/sinon.js',
|
||||
|
||||
// To make webpack happy
|
||||
// Related: https://github.com/request/request/issues/1529
|
||||
// (there's no mock available for fs, so we fake a mock by using
|
||||
// an in-memory version of fs)
|
||||
"fs": "memfs",
|
||||
},
|
||||
modules: [
|
||||
path.resolve('./test'),
|
||||
"node_modules"
|
||||
],
|
||||
},
|
||||
node: {
|
||||
// Because webpack is made of fail
|
||||
// https://github.com/request/request/issues/1529
|
||||
// Note: 'mock' is the new 'empty'
|
||||
net: 'mock',
|
||||
tls: 'mock'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
externals: {
|
||||
// Don't try to bundle electron: leave it as a commonjs dependency
|
||||
|
|
|
@ -76,7 +76,6 @@
|
|||
"lodash": "^4.13.1",
|
||||
"lolex": "2.3.2",
|
||||
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
|
||||
"memfs": "^2.10.1",
|
||||
"optimist": "^0.6.1",
|
||||
"pako": "^1.0.5",
|
||||
"prop-types": "^15.5.8",
|
||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
|||
1. for browsers that support native overlay auto-hiding scrollbars
|
||||
*/
|
||||
.mx_AutoHideScrollbar {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
}
|
||||
|
@ -34,23 +35,20 @@ body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar:hover {
|
|||
}
|
||||
/*
|
||||
3. as a last fallback, compensate for the scrollbar taking up space in the layout
|
||||
by playing with the paddings. the default below will add a right padding
|
||||
of the scrollbar width and clear that on hover.
|
||||
this won't work well on classes that also need to set their padding,
|
||||
so this needs to be overriden and adjust the padding with calc like so:
|
||||
```
|
||||
body.mx_scrollbar_nooverlay .componentClass.mx_AutoHideScrollbar_overflow:hover {
|
||||
padding-right: calc(15px - var(--scrollbar-width)) !important;
|
||||
}
|
||||
```
|
||||
by having giving the child element (.mx_AutoHideScrollbar_offset) a
|
||||
negative right margin of the width of the scrollbar when the container
|
||||
is overflowing. This is what Firefox ends up using. Overflow is detected
|
||||
in javascript, and adds the mx_AutoHideScrollbar_overflow class to the container.
|
||||
This only works in Firefox, which should be fine as this fallback is only needed there.
|
||||
*/
|
||||
body.mx_scrollbar_nooverlay .mx_AutoHideScrollbar {
|
||||
box-sizing: border-box;
|
||||
overflow-y: hidden;
|
||||
padding-right: var(--scrollbar-width);
|
||||
}
|
||||
|
||||
body.mx_scrollbar_nooverlay .mx_AutoHideScrollbar:hover {
|
||||
overflow-y: auto;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
body.mx_scrollbar_nooverlay .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow > .mx_AutoHideScrollbar_offset {
|
||||
margin-right: calc(-1 * var(--scrollbar-width));
|
||||
}
|
||||
|
|
|
@ -22,98 +22,74 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_RoomSubList_nonEmpty {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_labelContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
margin: 8px 19px 0 0;
|
||||
margin: 0 16px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_label {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_label > span {
|
||||
flex: 1 1 auto;
|
||||
text-transform: uppercase;
|
||||
color: $roomsublist-label-fg-color;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
margin-left: 16px;
|
||||
padding-left: 16px; /* gutter */
|
||||
padding-right: 16px; /* gutter */
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_label.mx_RoomSubList_fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
/* pointer-events: none; */
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_badge {
|
||||
height: 18px;
|
||||
border-radius: 9px;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 8px;
|
||||
color: $accent-fg-color;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
line-height: 18px;
|
||||
padding: 0 4px;
|
||||
padding: 0 5px;
|
||||
background-color: $accent-color;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_label .mx_RoomSubList_badge:hover {
|
||||
filter: brightness($focus-brightness);
|
||||
}
|
||||
|
||||
.mx_RoomSubList_addRoom, .mx_RoomSubList_badge {
|
||||
margin: 5px;
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_addRoom {
|
||||
background-color: $roomheader-addroom-color;
|
||||
color: $roomsublist-background;
|
||||
border-radius: 9px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 18px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-image: url('../../img/icons-room-add.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 10px; // 16/2 + 2 padding
|
||||
height: 16px;
|
||||
flex: 0 0 16px;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_badgeHighlight {
|
||||
background-color: $warning-color;
|
||||
}
|
||||
|
||||
/* This is the bottom of the speech bubble */
|
||||
.mx_RoomSubList_badgeHighlight:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
border-top: 5px solid $warning-color;
|
||||
border-right: 7px solid transparent;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_chevron {
|
||||
left: 0px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
width: 9px;
|
||||
height: 4px;
|
||||
background-image: url('../../img/topleft-chevron.svg');
|
||||
background-size: cover;
|
||||
// the transition doesn't work as the chevron gets remounted
|
||||
transition: rotateZ 0.2s ease-in;
|
||||
background-repeat: no-repeat;
|
||||
transition: transform 0.2s ease-in;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-position: center;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_chevronDown {
|
||||
|
@ -131,47 +107,26 @@ limitations under the License.
|
|||
.mx_RoomSubList_scroll {
|
||||
/* let rooms list grab all available space */
|
||||
flex: 0 1 auto;
|
||||
padding: 0 15px !important;
|
||||
}
|
||||
/*
|
||||
for browsers that don't support overlay scrollbars,
|
||||
subtract scrollbar width from right padding on hover when overflowing
|
||||
so the content doesn't jump when showing the scrollbars
|
||||
*/
|
||||
body.mx_scrollbar_nooverlay .mx_RoomSubList_scroll.mx_AutoHideScrollbar_overflow:hover {
|
||||
padding-right: calc(15px - var(--scrollbar-width)) !important;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
.mx_RoomSubList_label {
|
||||
height: 17px;
|
||||
width: 28px; /* collapsed LHS Panel width */
|
||||
|
||||
.mx_RoomSubList_scroll {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_labelContainer {
|
||||
width: 28px; /* collapsed LHS Panel width */
|
||||
margin-right: 14px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* Hide the bottom of speech bubble */
|
||||
.mx_RoomSubList_badgeHighlight:after {
|
||||
display: none;
|
||||
.mx_RoomSubList_addRoom {
|
||||
margin-left: 3px;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_line {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_moreBadge {
|
||||
position: static;
|
||||
margin-left: 16px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_ellipsis {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.mx_RoomSubList_more {
|
||||
.mx_RoomSubList_label > span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,30 @@ limitations under the License.
|
|||
cursor: pointer;
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
padding: 2px 12px;
|
||||
padding: 0 8px 0 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_RoomTile_menuButton {
|
||||
display: none;
|
||||
flex: 0 0 16px;
|
||||
height: 16px;
|
||||
background-image: url('../../img/icon_context.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
// toggle menuButton and badge on hover/menu displayed
|
||||
.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:hover, .mx_RoomTile_menuDisplayed {
|
||||
.mx_RoomTile_menuButton {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_RoomTile_badge {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_RoomTile_tooltip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
@ -62,14 +82,12 @@ limitations under the License.
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_RoomTile_invite {
|
||||
/* color: rgba(69, 69, 69, 0.5); */
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
.mx_RoomTile {
|
||||
margin: 2px;
|
||||
padding: 2px 0 2px 12px;
|
||||
margin: 0 2px;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mx_RoomTile_name {
|
||||
|
@ -77,57 +95,26 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.mx_RoomTile_badge {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
right: 8px;
|
||||
top: 2px;
|
||||
min-width: 12px;
|
||||
right: 6px;
|
||||
top: 0px;
|
||||
border-radius: 16px;
|
||||
padding: 0px 4px 0px 4px;
|
||||
z-index: 3;
|
||||
border: 0.18em solid $secondary-accent-color;
|
||||
}
|
||||
|
||||
/* Hide the bottom of speech bubble */
|
||||
.mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
||||
display: none;
|
||||
.mx_RoomTile_menuButton {
|
||||
display: none; //no design for this for now
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the bottom of the speech bubble */
|
||||
.mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
border-top: 5px solid $warning-color;
|
||||
border-right: 7px solid transparent;
|
||||
}
|
||||
|
||||
.mx_RoomTile_badge {
|
||||
flex: 0 1 content;
|
||||
min-width: 15px;
|
||||
border-radius: 8px;
|
||||
border-radius: 0.8em;
|
||||
padding: 0 0.4em;
|
||||
color: $accent-fg-color;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.mx_RoomTile .mx_RoomTile_badge.mx_RoomTile_badgeButton,
|
||||
.mx_RoomTile.mx_RoomTile_menuDisplayed .mx_RoomTile_badge {
|
||||
letter-spacing: 0.1em;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mx_RoomTile.mx_RoomTile_noBadges .mx_RoomTile_badge.mx_RoomTile_badgeButton,
|
||||
.mx_RoomTile.mx_RoomTile_menuDisplayed.mx_RoomTile_noBadges .mx_RoomTile_badge {
|
||||
background-color: $neutral-badge-color;
|
||||
}
|
||||
|
||||
.mx_RoomTile_unreadNotify .mx_RoomTile_badge {
|
||||
|
@ -169,10 +156,6 @@ limitations under the License.
|
|||
background-color: $roomtile-focused-bg-color;
|
||||
}
|
||||
|
||||
.mx_RoomTile .mx_RoomTile_name.mx_RoomTile_badgeShown {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.mx_RoomTile_arrow {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
|
|
|
@ -1,23 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg4196" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 25 25"
|
||||
style="enable-background:new 0 0 25 25;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st1{opacity:0.7;}
|
||||
.st2{fill:none;stroke-linecap:round;}
|
||||
</style>
|
||||
<g id="icons_people" transform="translate(50.000000, 725.000000)">
|
||||
<path id="Oval-1-Copy-7" fill="#76CFA6" d="M-37.5-700c6.9,0,12.5-5.6,12.5-12.5S-30.6-725-37.5-725S-50-719.4-50-712.5
|
||||
S-44.4-700-37.5-700z"/>
|
||||
<g id="text3879" transform="matrix(1.0243293,0,0,0.97624855,-24.996028,0.15844144)">
|
||||
<g id="Group-3" transform="translate(14.4375,3.9375)" class="st1">
|
||||
<path id="Line" class="st2" stroke="#ffffff" d="M-23.2-733.8h4.6"/>
|
||||
<path id="path3142" class="st2" stroke="#ffffff" d="M-20.9-736.2v4.8"/>
|
||||
</g>
|
||||
<path id="path3002" fill="#ffffff" d="M-11.4-731.3l-0.5,2.6h2.2v1h-2.4l-0.7,3.3h-1.1l0.7-3.3
|
||||
h-2.3l-0.6,3.3h-1.1l0.6-3.3h-2v-1h2.2l0.5-2.6H-18v-1h2.3l0.6-3.4h1.1l-0.6,3.4h2.4l0.7-3.4h1l-0.7,3.4h2v1H-11.4 M-15.3-728.7
|
||||
h2.3l0.5-2.6h-2.3L-15.3-728.7"/>
|
||||
</g>
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="11.521363"
|
||||
height="11.521363"
|
||||
viewBox="0 0 11.521363 11.521363"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="icons-room-add.svg"
|
||||
style="fill:none"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1586"
|
||||
inkscape:window-height="988"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
fit-margin-top="2"
|
||||
fit-margin-left="2"
|
||||
fit-margin-right="2"
|
||||
fit-margin-bottom="2"
|
||||
inkscape:zoom="29.5"
|
||||
inkscape:cx="5.8284785"
|
||||
inkscape:cy="5.7606831"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="m 5.7606819,2.7606819 v 6"
|
||||
id="path2"
|
||||
inkscape:connector-curvature="0"
|
||||
style="stroke:#ffffff;stroke-width:1.5;stroke-linecap:round" />
|
||||
<g
|
||||
style="fill:none"
|
||||
id="g876"
|
||||
transform="translate(1.7606819,4.7606819)">
|
||||
<path
|
||||
id="path865"
|
||||
d="M 7,1 H 1"
|
||||
inkscape:connector-curvature="0"
|
||||
style="stroke:#ffffff;stroke-width:1.5;stroke-linecap:round" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.0 KiB |
|
@ -1,17 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="6px" viewBox="0 0 10 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>dropdown</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<g id="matrix-my-stuff-no-lines-message-context-menu-smaller-icons" transform="translate(-203.000000, -25.000000)" stroke="#212121" stroke-width="1.3">
|
||||
<g id="Group-3" transform="translate(128.000000, 15.000000)">
|
||||
<g id="dropdown" transform="translate(76.000000, 11.000000)">
|
||||
<path d="M0.5,0.5 L4.35868526,3.75422271" id="Line-5"></path>
|
||||
<path d="M8.13193273,0.560421385 L4.35868526,3.75422271" id="Line-5-Copy"></path>
|
||||
</g>
|
||||
</g>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="9.946106"
|
||||
height="5.5662179"
|
||||
viewBox="0 0 9.946106 5.5662179"
|
||||
version="1.1"
|
||||
id="svg14"
|
||||
sodipodi:docname="topleft-chevron.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata
|
||||
id="metadata18">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>dropdown</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1277"
|
||||
inkscape:window-height="653"
|
||||
id="namedview16"
|
||||
showgrid="false"
|
||||
fit-margin-top="0.5"
|
||||
fit-margin-left="0.5"
|
||||
fit-margin-right="0.5"
|
||||
fit-margin-bottom="0.5"
|
||||
inkscape:zoom="35.2"
|
||||
inkscape:cx="4.6570922"
|
||||
inkscape:cy="2.9102278"
|
||||
inkscape:window-x="459"
|
||||
inkscape:window-y="90"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg14" />
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title
|
||||
id="title2">dropdown</title>
|
||||
<desc
|
||||
id="desc4">Created with Sketch.</desc>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<g
|
||||
id="Page-1"
|
||||
style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round"
|
||||
transform="translate(-0.3429078,-0.34400861)">
|
||||
<g
|
||||
id="matrix-my-stuff-no-lines-message-context-menu-smaller-icons"
|
||||
transform="translate(-203,-25)"
|
||||
style="stroke:#212121;stroke-width:1.29999995">
|
||||
<g
|
||||
id="Group-3"
|
||||
transform="translate(128,15)">
|
||||
<g
|
||||
id="dropdown"
|
||||
transform="translate(76,11)">
|
||||
<path
|
||||
d="M 0.5,0.5 4.3586853,3.7542227"
|
||||
id="Line-5"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="M 8.1319327,0.56042139 4.3586853,3.7542227"
|
||||
id="Line-5-Copy"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1017 B After Width: | Height: | Size: 2.6 KiB |
|
@ -62,6 +62,35 @@ function createAccountDataAction(matrixClient, accountDataEvent) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAccountDataAction
|
||||
* @type {Object}
|
||||
* @property {string} action 'MatrixActions.Room.accountData'.
|
||||
* @property {MatrixEvent} event the MatrixEvent that triggered the dispatch.
|
||||
* @property {string} event_type the type of the MatrixEvent, e.g. "m.direct".
|
||||
* @property {Object} event_content the content of the MatrixEvent.
|
||||
* @property {Room} room the room where the account data was changed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a MatrixActions.Room.accountData action that represents a MatrixClient `Room.accountData`
|
||||
* matrix event.
|
||||
*
|
||||
* @param {MatrixClient} matrixClient the matrix client.
|
||||
* @param {MatrixEvent} accountDataEvent the account data event.
|
||||
* @param {Room} room the room where account data was changed
|
||||
* @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData.
|
||||
*/
|
||||
function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
|
||||
return {
|
||||
action: 'MatrixActions.Room.accountData',
|
||||
event: accountDataEvent,
|
||||
event_type: accountDataEvent.getType(),
|
||||
event_content: accountDataEvent.getContent(),
|
||||
room: room,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RoomAction
|
||||
* @type {Object}
|
||||
|
@ -201,6 +230,7 @@ export default {
|
|||
start(matrixClient) {
|
||||
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction);
|
||||
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room', createRoomAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
|
||||
this._addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
|
||||
|
|
|
@ -112,7 +112,9 @@ export default class AutoHideScrollbar extends React.Component {
|
|||
ref={this._collectContainerRef}
|
||||
className={["mx_AutoHideScrollbar", this.props.className].join(" ")}
|
||||
>
|
||||
{ this.props.children }
|
||||
<div className="mx_AutoHideScrollbar_offset">
|
||||
{ this.props.children }
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,8 +151,7 @@ const LeftPanel = React.createClass({
|
|||
}
|
||||
} while (element && !(
|
||||
classes.contains("mx_RoomTile") ||
|
||||
classes.contains("mx_SearchBox_search") ||
|
||||
classes.contains("mx_RoomSubList_ellipsis")));
|
||||
classes.contains("mx_SearchBox_search")));
|
||||
|
||||
if (element) {
|
||||
element.focus();
|
||||
|
|
|
@ -243,9 +243,8 @@ const RoomSubList = React.createClass({
|
|||
const subListNotifCount = subListNotifications[0];
|
||||
const subListNotifHighlight = subListNotifications[1];
|
||||
|
||||
|
||||
let badge;
|
||||
if (this.state.hidden) {
|
||||
if (!this.props.collapsed) {
|
||||
const badgeClasses = classNames({
|
||||
'mx_RoomSubList_badge': true,
|
||||
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
||||
|
@ -285,9 +284,7 @@ const RoomSubList = React.createClass({
|
|||
let addRoomButton;
|
||||
if (this.props.onAddRoom) {
|
||||
addRoomButton = (
|
||||
<AccessibleButton onClick={ this.props.onAddRoom } className="mx_RoomSubList_addRoom">
|
||||
+
|
||||
</AccessibleButton>
|
||||
<AccessibleButton onClick={ this.props.onAddRoom } className="mx_RoomSubList_addRoom" />
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -307,7 +304,7 @@ const RoomSubList = React.createClass({
|
|||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
||||
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
|
||||
{ chevron }
|
||||
{ this.props.collapsed ? '' : this.props.label }
|
||||
<span>{this.props.label}</span>
|
||||
{ incomingCall }
|
||||
</AccessibleButton>
|
||||
{ badge }
|
||||
|
|
|
@ -82,6 +82,8 @@ const SIMPLE_SETTINGS = [
|
|||
{ id: "TagPanel.disableTagPanel" },
|
||||
{ id: "enableWidgetScreenshots" },
|
||||
{ id: "RoomSubList.showEmpty" },
|
||||
{ id: "pinMentionedRooms" },
|
||||
{ id: "pinUnreadRooms" },
|
||||
{ id: "showDeveloperTools" },
|
||||
];
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ module.exports = React.createClass({
|
|||
this.setState( { badgeHover: false } );
|
||||
},
|
||||
|
||||
onBadgeClicked: function(e) {
|
||||
onOpenMenu: function(e) {
|
||||
// Prevent the RoomTile onClick event firing as well
|
||||
e.stopPropagation();
|
||||
// Only allow non-guests to access the context menu
|
||||
|
@ -276,19 +276,14 @@ module.exports = React.createClass({
|
|||
if (name == undefined || name == null) name = '';
|
||||
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
||||
|
||||
let badgeContent;
|
||||
|
||||
if (this.state.badgeHover || this.state.menuDisplayed) {
|
||||
badgeContent = "\u00B7\u00B7\u00B7";
|
||||
} else if (badges) {
|
||||
let badge;
|
||||
if (badges) {
|
||||
const limitedCount = FormattingUtils.formatCount(notificationCount);
|
||||
badgeContent = notificationCount ? limitedCount : '!';
|
||||
} else {
|
||||
badgeContent = '\u200B';
|
||||
const badgeContent = notificationCount ? limitedCount : '!';
|
||||
badge = <div className={badgeClasses}>{ badgeContent }</div>;
|
||||
}
|
||||
|
||||
const badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>;
|
||||
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
let label;
|
||||
let tooltip;
|
||||
|
@ -317,6 +312,11 @@ module.exports = React.createClass({
|
|||
// incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
|
||||
//}
|
||||
|
||||
let contextMenuButton;
|
||||
if (!MatrixClientPeg.get().isGuest()) {
|
||||
contextMenuButton = <AccessibleButton className="mx_RoomTile_menuButton" onClick={this.onOpenMenu} />;
|
||||
}
|
||||
|
||||
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
||||
|
||||
let dmIndicator;
|
||||
|
@ -338,6 +338,7 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
{ label }
|
||||
{ contextMenuButton }
|
||||
{ badge }
|
||||
{ /* { incomingCallBox } */ }
|
||||
{ tooltip }
|
||||
|
|
|
@ -249,6 +249,8 @@
|
|||
"Enable URL previews for this room (only affects you)": "Enable URL previews for this room (only affects you)",
|
||||
"Enable URL previews by default for participants in this room": "Enable URL previews by default for participants in this room",
|
||||
"Room Colour": "Room Colour",
|
||||
"Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list",
|
||||
"Pin rooms I'm mentioned in to the top of the room list": "Pin rooms I'm mentioned in to the top of the room list",
|
||||
"Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets",
|
||||
"Show empty room list headings": "Show empty room list headings",
|
||||
"Collecting app version information": "Collecting app version information",
|
||||
|
|
|
@ -276,6 +276,16 @@ export const SETTINGS = {
|
|||
default: true,
|
||||
controller: new AudioNotificationsEnabledController(),
|
||||
},
|
||||
"pinMentionedRooms": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Pin rooms I'm mentioned in to the top of the room list"),
|
||||
default: false,
|
||||
},
|
||||
"pinUnreadRooms": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Pin unread rooms to the top of the room list"),
|
||||
default: false,
|
||||
},
|
||||
"enableWidgetScreenshots": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td('Enable widget screenshots on supported widgets'),
|
||||
|
|
|
@ -17,6 +17,7 @@ import {Store} from 'flux/utils';
|
|||
import dis from '../dispatcher';
|
||||
import DMRoomMap from '../utils/DMRoomMap';
|
||||
import Unread from '../Unread';
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
/**
|
||||
* A class for storing application state for categorising rooms in
|
||||
|
@ -53,6 +54,24 @@ class RoomListStore extends Store {
|
|||
"im.vector.fake.archived": [],
|
||||
},
|
||||
ready: false,
|
||||
|
||||
// The room cache stores a mapping of roomId to cache record.
|
||||
// Each cache record is a key/value pair for various bits of
|
||||
// data used to sort the room list. Currently this stores the
|
||||
// following bits of informations:
|
||||
// "timestamp": number, The timestamp of the last relevant
|
||||
// event in the room.
|
||||
// "notifications": boolean, Whether or not the user has been
|
||||
// highlighted on any unread events.
|
||||
// "unread": boolean, Whether or not the user has any
|
||||
// unread events.
|
||||
//
|
||||
// All of the cached values are lazily loaded on read in the
|
||||
// recents comparator. When an event is received for a particular
|
||||
// room, all the cached values are invalidated - forcing the
|
||||
// next read to set new values. The entries do not expire on
|
||||
// their own.
|
||||
roomCache: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,6 +103,8 @@ class RoomListStore extends Store {
|
|||
!payload.isLiveUnfilteredRoomTimelineEvent ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
|
||||
this._clearCachedRoomState(payload.event.getRoomId());
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
|
@ -111,6 +132,8 @@ class RoomListStore extends Store {
|
|||
if (liveTimeline !== eventTimeline ||
|
||||
!this._eventTriggersRecentReorder(payload.event)
|
||||
) break;
|
||||
|
||||
this._clearCachedRoomState(payload.event.getRoomId());
|
||||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
|
@ -119,6 +142,13 @@ class RoomListStore extends Store {
|
|||
this._generateRoomLists();
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.accountData': {
|
||||
if (payload.event_type === 'm.fully_read') {
|
||||
this._clearCachedRoomState(payload.room.roomId);
|
||||
this._generateRoomLists();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'MatrixActions.Room.myMembership': {
|
||||
this._generateRoomLists();
|
||||
}
|
||||
|
@ -216,11 +246,18 @@ class RoomListStore extends Store {
|
|||
}
|
||||
});
|
||||
|
||||
// Note: we check the settings up here instead of in the forEach or
|
||||
// in the _recentsComparator to avoid hitting the SettingsStore a few
|
||||
// thousand times.
|
||||
const pinUnread = SettingsStore.getValue("pinUnreadRooms");
|
||||
const pinMentioned = SettingsStore.getValue("pinMentionedRooms");
|
||||
Object.keys(lists).forEach((listKey) => {
|
||||
let comparator;
|
||||
switch (RoomListStore._listOrders[listKey]) {
|
||||
case "recent":
|
||||
comparator = this._recentsComparator;
|
||||
comparator = (roomA, roomB) => {
|
||||
return this._recentsComparator(roomA, roomB, pinUnread, pinMentioned);
|
||||
};
|
||||
break;
|
||||
case "manual":
|
||||
default:
|
||||
|
@ -236,6 +273,44 @@ class RoomListStore extends Store {
|
|||
});
|
||||
}
|
||||
|
||||
_updateCachedRoomState(roomId, type, value) {
|
||||
const roomCache = this._state.roomCache;
|
||||
if (!roomCache[roomId]) roomCache[roomId] = {};
|
||||
|
||||
if (value) roomCache[roomId][type] = value;
|
||||
else delete roomCache[roomId][type];
|
||||
|
||||
this._setState({roomCache});
|
||||
}
|
||||
|
||||
_clearCachedRoomState(roomId) {
|
||||
const roomCache = this._state.roomCache;
|
||||
delete roomCache[roomId];
|
||||
this._setState({roomCache});
|
||||
}
|
||||
|
||||
_getRoomState(room, type) {
|
||||
const roomId = room.roomId;
|
||||
const roomCache = this._state.roomCache;
|
||||
if (roomCache[roomId] && typeof roomCache[roomId][type] !== 'undefined') {
|
||||
return roomCache[roomId][type];
|
||||
}
|
||||
|
||||
if (type === "timestamp") {
|
||||
const ts = this._tsOfNewestEvent(room);
|
||||
this._updateCachedRoomState(roomId, "timestamp", ts);
|
||||
return ts;
|
||||
} else if (type === "unread") {
|
||||
const unread = room.getUnreadNotificationCount() > 0;
|
||||
this._updateCachedRoomState(roomId, "unread", unread);
|
||||
return unread;
|
||||
} else if (type === "notifications") {
|
||||
const notifs = room.getUnreadNotificationCount("highlight") > 0;
|
||||
this._updateCachedRoomState(roomId, "notifications", notifs);
|
||||
return notifs;
|
||||
} else throw new Error("Unrecognized room cache type: " + type);
|
||||
}
|
||||
|
||||
_eventTriggersRecentReorder(ev) {
|
||||
return ev.getTs() && (
|
||||
Unread.eventTriggersUnreadCount(ev) ||
|
||||
|
@ -261,10 +336,40 @@ class RoomListStore extends Store {
|
|||
}
|
||||
}
|
||||
|
||||
_recentsComparator(roomA, roomB) {
|
||||
// XXX: We could use a cache here and update it when we see new
|
||||
// events that trigger a reorder
|
||||
return this._tsOfNewestEvent(roomB) - this._tsOfNewestEvent(roomA);
|
||||
_recentsComparator(roomA, roomB, pinUnread, pinMentioned) {
|
||||
// We try and set the ordering to be Mentioned > Unread > Recent
|
||||
// assuming the user has the right settings, of course.
|
||||
|
||||
const timestampA = this._getRoomState(roomA, "timestamp");
|
||||
const timestampB = this._getRoomState(roomB, "timestamp");
|
||||
const timestampDiff = timestampB - timestampA;
|
||||
|
||||
if (pinMentioned) {
|
||||
const mentionsA = this._getRoomState(roomA, "notifications");
|
||||
const mentionsB = this._getRoomState(roomB, "notifications");
|
||||
if (mentionsA && !mentionsB) return -1;
|
||||
if (!mentionsA && mentionsB) return 1;
|
||||
|
||||
// If they both have notifications, sort by timestamp.
|
||||
// If neither have notifications (the fourth check not shown
|
||||
// here), then try and sort by unread messages and finally by
|
||||
// timestamp.
|
||||
if (mentionsA && mentionsB) return timestampDiff;
|
||||
}
|
||||
|
||||
if (pinUnread) {
|
||||
const unreadA = this._getRoomState(roomA, "unread");
|
||||
const unreadB = this._getRoomState(roomB, "unread");
|
||||
if (unreadA && !unreadB) return -1;
|
||||
if (!unreadA && unreadB) return 1;
|
||||
|
||||
// If they both have unread messages, sort by timestamp
|
||||
// If nether have unread message (the fourth check not shown
|
||||
// here), then just sort by timestamp anyways.
|
||||
if (unreadA && unreadB) return timestampDiff;
|
||||
}
|
||||
|
||||
return timestampDiff;
|
||||
}
|
||||
|
||||
_lexicographicalComparator(roomA, roomB) {
|
||||
|
|
Loading…
Reference in New Issue