Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
matrix-react-sdk
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Model registry
Analyze
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GitHub Mirror
Matrix.org
matrix-react-sdk
Commits
a7dc7b82
Unverified
Commit
a7dc7b82
authored
11 months ago
by
R Midhun Suresh
Browse files
Options
Downloads
Patches
Plain Diff
WIP: Convert from class -> functional components
parent
4467768d
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/components/views/rooms/EntityTileRefactored.tsx
+169
-0
169 additions, 0 deletions
src/components/views/rooms/EntityTileRefactored.tsx
src/components/views/rooms/MemberTileNext.tsx
+175
-0
175 additions, 0 deletions
src/components/views/rooms/MemberTileNext.tsx
with
344 additions
and
0 deletions
src/components/views/rooms/EntityTileRefactored.tsx
0 → 100644
+
169
−
0
View file @
a7dc7b82
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
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
React
,
{
useCallback
}
from
"
react
"
;
import
classNames
from
"
classnames
"
;
import
AccessibleButton
from
"
../elements/AccessibleButton
"
;
import
{
_t
,
_td
,
TranslationKey
}
from
"
../../../languageHandler
"
;
import
E2EIcon
from
"
./E2EIcon
"
;
import
{
E2EState
}
from
"
../../../models/rooms/E2EState
"
;
import
BaseAvatar
from
"
../avatars/BaseAvatar
"
;
import
PresenceLabel
from
"
./PresenceLabel
"
;
import
{
PresenceState
}
from
"
../../../models/rooms/PresenceState
"
;
export
enum
PowerStatus
{
Admin
=
"
admin
"
,
Moderator
=
"
moderator
"
,
}
const
PowerLabel
:
Record
<
PowerStatus
,
TranslationKey
>
=
{
[
PowerStatus
.
Admin
]:
_td
(
"
power_level|admin
"
),
[
PowerStatus
.
Moderator
]:
_td
(
"
power_level|mod
"
),
};
const
PRESENCE_CLASS
:
Record
<
PresenceState
,
string
>
=
{
"
offline
"
:
"
mx_EntityTile_offline
"
,
"
online
"
:
"
mx_EntityTile_online
"
,
"
unavailable
"
:
"
mx_EntityTile_unavailable
"
,
"
io.element.unreachable
"
:
"
mx_EntityTile_unreachable
"
,
};
function
presenceClassForMember
(
presenceState
?:
PresenceState
,
lastActiveAgo
?:
number
,
showPresence
?:
boolean
):
string
{
if
(
showPresence
===
false
)
{
return
"
mx_EntityTile_online_beenactive
"
;
}
// offline is split into two categories depending on whether we have
// a last_active_ago for them.
if
(
presenceState
===
"
offline
"
)
{
if
(
lastActiveAgo
)
{
return
PRESENCE_CLASS
[
"
offline
"
]
+
"
_beenactive
"
;
}
else
{
return
PRESENCE_CLASS
[
"
offline
"
]
+
"
_neveractive
"
;
}
}
else
if
(
presenceState
)
{
return
PRESENCE_CLASS
[
presenceState
];
}
else
{
return
PRESENCE_CLASS
[
"
offline
"
]
+
"
_neveractive
"
;
}
}
interface
IProps
{
name
?:
string
;
nameJSX
?:
JSX
.
Element
;
title
?:
string
;
avatarJsx
?:
JSX
.
Element
;
// <BaseAvatar />
className
?:
string
;
presenceState
?:
PresenceState
;
presenceLastActiveAgo
:
number
;
presenceLastTs
:
number
;
presenceCurrentlyActive
?:
boolean
;
showInviteButton
?:
boolean
;
onClick
():
void
;
showPresence
?:
boolean
;
subtextLabel
?:
string
;
e2eStatus
?:
E2EState
;
powerStatus
?:
PowerStatus
;
}
export
default
function
EntityTileRefactored
({
onClick
=
()
=>
{},
presenceState
=
"
offline
"
,
presenceLastActiveAgo
=
0
,
presenceLastTs
=
0
,
showInviteButton
=
false
,
showPresence
=
true
,
...
props
}:
IProps
):
JSX
.
Element
{
/**
* Creates the PresenceLabel component if needed
* @returns The PresenceLabel component if we need to render it, undefined otherwise
*/
const
getPresenceLabel
=
useCallback
(():
JSX
.
Element
|
undefined
=>
{
if
(
!
showPresence
)
return
;
const
activeAgo
=
presenceLastActiveAgo
?
Date
.
now
()
-
(
presenceLastTs
-
presenceLastActiveAgo
)
:
-
1
;
return
(
<
PresenceLabel
activeAgo
=
{
activeAgo
}
currentlyActive
=
{
props
.
presenceCurrentlyActive
}
presenceState
=
{
presenceState
}
/>
);
},
[
presenceLastTs
,
presenceLastActiveAgo
,
presenceState
,
props
.
presenceCurrentlyActive
,
showPresence
]);
const
mainClassNames
:
Record
<
string
,
boolean
>
=
{
mx_EntityTile
:
true
,
};
if
(
props
.
className
)
mainClassNames
[
props
.
className
]
=
true
;
const
presenceClass
=
presenceClassForMember
(
presenceState
,
presenceLastActiveAgo
,
showPresence
);
mainClassNames
[
presenceClass
]
=
true
;
const
name
=
props
.
nameJSX
||
props
.
name
;
const
nameAndPresence
=
(
<
div
className
=
"mx_EntityTile_details"
>
<
div
className
=
"mx_EntityTile_name"
>
{
name
}
</
div
>
{
getPresenceLabel
()
}
</
div
>
);
let
inviteButton
;
if
(
showInviteButton
)
{
inviteButton
=
(
<
div
className
=
"mx_EntityTile_invite"
>
<
img
alt
=
{
_t
(
"
action|invite
"
)
}
src
=
{
require
(
"
../../../../res/img/plus.svg
"
).
default
}
width
=
"16"
height
=
"16"
/>
</
div
>
);
}
let
powerLabel
;
const
powerStatus
=
props
.
powerStatus
;
if
(
powerStatus
)
{
const
powerText
=
_t
(
PowerLabel
[
powerStatus
]);
powerLabel
=
<
div
className
=
"mx_EntityTile_power"
>
{
powerText
}
</
div
>;
}
let
e2eIcon
;
const
{
e2eStatus
}
=
props
;
if
(
e2eStatus
)
{
e2eIcon
=
<
E2EIcon
status
=
{
e2eStatus
}
isUser
=
{
true
}
bordered
=
{
true
}
/>;
}
const
av
=
props
.
avatarJsx
||
<
BaseAvatar
name
=
{
props
.
name
}
size
=
"36px"
aria-hidden
=
"true"
/>;
// The wrapping div is required to make the magic mouse listener work, for some reason.
return
(
<
div
>
<
AccessibleButton
className
=
{
classNames
(
mainClassNames
)
}
title
=
{
props
.
title
}
onClick
=
{
onClick
}
>
<
div
className
=
"mx_EntityTile_avatar"
>
{
av
}
{
e2eIcon
}
</
div
>
{
nameAndPresence
}
{
powerLabel
}
{
inviteButton
}
</
AccessibleButton
>
</
div
>
);
}
This diff is collapsed.
Click to expand it.
src/components/views/rooms/MemberTileNext.tsx
0 → 100644
+
175
−
0
View file @
a7dc7b82
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019, 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
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
{
RoomStateEvent
,
MatrixEvent
,
EventType
}
from
"
matrix-js-sdk/src/matrix
"
;
import
{
DeviceInfo
}
from
"
matrix-js-sdk/src/crypto/deviceinfo
"
;
import
{
CryptoEvent
}
from
"
matrix-js-sdk/src/crypto
"
;
import
{
UserVerificationStatus
}
from
"
matrix-js-sdk/src/crypto-api
"
;
import
dis
from
"
../../../dispatcher/dispatcher
"
;
import
{
_t
}
from
"
../../../languageHandler
"
;
import
{
MatrixClientPeg
}
from
"
../../../MatrixClientPeg
"
;
import
{
Action
}
from
"
../../../dispatcher/actions
"
;
import
{
PowerStatus
}
from
"
./EntityTile
"
;
import
DisambiguatedProfile
from
"
../messages/DisambiguatedProfile
"
;
import
UserIdentifierCustomisations
from
"
../../../customisations/UserIdentifier
"
;
import
{
E2EState
}
from
"
../../../models/rooms/E2EState
"
;
import
{
asyncSome
}
from
"
../../../utils/arrays
"
;
import
{
getUserDeviceIds
}
from
"
../../../utils/crypto/deviceInfo
"
;
import
{
RoomMember
}
from
"
../../../models/rooms/RoomMember
"
;
import
MemberAvatarNext
from
"
../avatars/MemberAvatarNext
"
;
import
EntityTileRefactored
from
"
./EntityTileRefactored
"
;
interface
IProps
{
member
:
RoomMember
;
showPresence
?:
boolean
;
}
export
default
function
MemberTile
(
props
:
IProps
):
JSX
.
Element
{
// const [isRoomEncrypted, setIsRoomEncrypted] = useState(false);
const
[
e2eStatus
,
setE2eStatus
]
=
useState
<
E2EState
|
undefined
>
();
useEffect
(()
=>
{
const
cli
=
MatrixClientPeg
.
safeGet
();
const
updateE2EStatus
=
async
():
Promise
<
void
>
=>
{
const
{
userId
}
=
props
.
member
;
const
isMe
=
userId
===
cli
.
getUserId
();
const
userTrust
=
await
cli
.
getCrypto
()?.
getUserVerificationStatus
(
userId
);
if
(
!
userTrust
?.
isCrossSigningVerified
())
{
setE2eStatus
(
userTrust
?.
wasCrossSigningVerified
()
?
E2EState
.
Warning
:
E2EState
.
Normal
);
return
;
}
const
deviceIDs
=
await
getUserDeviceIds
(
cli
,
userId
);
const
anyDeviceUnverified
=
await
asyncSome
(
deviceIDs
,
async
(
deviceId
)
=>
{
// For your own devices, we use the stricter check of cross-signing
// verification to encourage everyone to trust their own devices via
// cross-signing so that other users can then safely trust you.
// For other people's devices, the more general verified check that
// includes locally verified devices can be used.
const
deviceTrust
=
await
cli
.
getCrypto
()?.
getDeviceVerificationStatus
(
userId
,
deviceId
);
return
!
deviceTrust
||
(
isMe
?
!
deviceTrust
.
crossSigningVerified
:
!
deviceTrust
.
isVerified
());
});
setE2eStatus
(
anyDeviceUnverified
?
E2EState
.
Warning
:
E2EState
.
Verified
);
};
const
onRoomStateEvents
=
(
ev
:
MatrixEvent
):
void
=>
{
if
(
ev
.
getType
()
!==
EventType
.
RoomEncryption
)
return
;
const
{
roomId
}
=
props
.
member
;
if
(
ev
.
getRoomId
()
!==
roomId
)
return
;
// The room is encrypted now.
cli
.
removeListener
(
RoomStateEvent
.
Events
,
onRoomStateEvents
);
updateE2EStatus
();
};
const
onUserTrustStatusChanged
=
(
userId
:
string
,
trustStatus
:
UserVerificationStatus
):
void
=>
{
if
(
userId
!==
props
.
member
.
userId
)
return
;
updateE2EStatus
();
};
const
onDeviceVerificationChanged
=
(
userId
:
string
,
deviceId
:
string
,
deviceInfo
:
DeviceInfo
):
void
=>
{
if
(
userId
!==
props
.
member
.
userId
)
return
;
updateE2EStatus
();
};
const
{
roomId
}
=
props
.
member
;
if
(
roomId
)
{
const
isRoomEncrypted
=
cli
.
isRoomEncrypted
(
roomId
);
if
(
isRoomEncrypted
)
{
cli
.
on
(
CryptoEvent
.
UserTrustStatusChanged
,
onUserTrustStatusChanged
);
cli
.
on
(
CryptoEvent
.
DeviceVerificationChanged
,
onDeviceVerificationChanged
);
updateE2EStatus
();
}
else
{
// Listen for room to become encrypted
cli
.
on
(
RoomStateEvent
.
Events
,
onRoomStateEvents
);
}
}
return
()
=>
{
if
(
cli
)
{
cli
.
removeListener
(
RoomStateEvent
.
Events
,
onRoomStateEvents
);
cli
.
removeListener
(
CryptoEvent
.
UserTrustStatusChanged
,
onUserTrustStatusChanged
);
cli
.
removeListener
(
CryptoEvent
.
DeviceVerificationChanged
,
onDeviceVerificationChanged
);
}
};
},
[
props
.
member
]);
const
onClick
=
():
void
=>
{
dis
.
dispatch
({
action
:
Action
.
ViewUser
,
member
:
props
.
member
,
push
:
true
,
});
};
const
getDisplayName
=
():
string
=>
{
return
props
.
member
.
name
;
};
const
getPowerLabel
=
():
string
=>
{
return
_t
(
"
member_list|power_label
"
,
{
userName
:
UserIdentifierCustomisations
.
getDisplayUserIdentifier
(
props
.
member
.
userId
,
{
roomId
:
props
.
member
.
roomId
,
}),
powerLevelNumber
:
props
.
member
.
powerLevel
,
}).
trim
();
};
const
member
=
props
.
member
;
const
name
=
getDisplayName
();
const
av
=
<
MemberAvatarNext
member
=
{
member
}
size
=
"36px"
aria-hidden
=
"true"
/>;
const
powerStatusMap
=
new
Map
([
[
100
,
PowerStatus
.
Admin
],
[
50
,
PowerStatus
.
Moderator
],
]);
// Find the nearest power level with a badge
let
powerLevel
=
props
.
member
.
powerLevel
;
for
(
const
[
pl
]
of
powerStatusMap
)
{
if
(
props
.
member
.
powerLevel
>=
pl
)
{
powerLevel
=
pl
;
break
;
}
}
const
powerStatus
=
powerStatusMap
.
get
(
powerLevel
);
const
nameJSX
=
<
DisambiguatedProfile
member
=
{
member
}
fallbackName
=
{
name
||
""
}
/>;
return
(
<
EntityTileRefactored
{
...
props
}
presenceState
=
{
member
.
presence
?.
state
}
presenceLastActiveAgo
=
{
member
.
presence
?.
lastActiveAgo
||
0
}
presenceLastTs
=
{
member
.
presence
?.
lastPresenceTime
||
0
}
presenceCurrentlyActive
=
{
member
.
presence
?.
currentlyActive
||
false
}
avatarJsx
=
{
av
}
title
=
{
getPowerLabel
()
}
name
=
{
name
}
nameJSX
=
{
nameJSX
}
powerStatus
=
{
powerStatus
}
showPresence
=
{
props
.
showPresence
}
e2eStatus
=
{
e2eStatus
}
onClick
=
{
onClick
}
/>
);
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment