You've already forked focalboard
mirror of
https://github.com/mattermost/focalboard.git
synced 2025-07-15 23:54:29 +02:00
User updates (#3244)
* retrieve additional user fields * implement config setting * cleanup * fix tests * remove unused constant * fix more tests * add tests, cleanup * lint fixes * revert bad lint fixes * more merge fixes * update to use personal setting * add user settings * update package-lock.json * lint fixes * npm audit fix * update tests * Revert "lint fixes" This reverts commit6a50d335ca
. * Revert "update package-lock.json" This reverts commit1117e557e4
. * Revert "npm audit fix" This reverts commit77ea931c77
. * Revert "Revert "lint fixes"" This reverts commit3ebd81b9fa
. * restore to original * fix for empty prefs * fix bad merge Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
@ -96,6 +96,7 @@ func (p *Plugin) OnConfigurationChange() error {
|
|||||||
}
|
}
|
||||||
p.server.Config().EnableDataRetention = enableBoardsDeletion
|
p.server.Config().EnableDataRetention = enableBoardsDeletion
|
||||||
p.server.Config().DataRetentionDays = *mmconfig.DataRetentionSettings.BoardsRetentionDays
|
p.server.Config().DataRetentionDays = *mmconfig.DataRetentionSettings.BoardsRetentionDays
|
||||||
|
p.server.Config().TeammateNameDisplay = *mmconfig.TeamSettings.TeammateNameDisplay
|
||||||
|
|
||||||
p.server.UpdateAppConfig()
|
p.server.UpdateAppConfig()
|
||||||
p.wsPluginAdapter.BroadcastConfigChange(*p.server.App().GetClientConfig())
|
p.wsPluginAdapter.BroadcastConfigChange(*p.server.App().GetClientConfig())
|
||||||
|
@ -62,11 +62,16 @@ func TestOnConfigurationChange(t *testing.T) {
|
|||||||
baseDataRetentionSettings := &serverModel.DataRetentionSettings{
|
baseDataRetentionSettings := &serverModel.DataRetentionSettings{
|
||||||
BoardsRetentionDays: &intRef,
|
BoardsRetentionDays: &intRef,
|
||||||
}
|
}
|
||||||
|
usernameRef := "username"
|
||||||
|
baseTeamSettings := &serverModel.TeamSettings{
|
||||||
|
TeammateNameDisplay: &usernameRef,
|
||||||
|
}
|
||||||
|
|
||||||
baseConfig := &serverModel.Config{
|
baseConfig := &serverModel.Config{
|
||||||
FeatureFlags: baseFeatureFlags,
|
FeatureFlags: baseFeatureFlags,
|
||||||
PluginSettings: *basePluginSettings,
|
PluginSettings: *basePluginSettings,
|
||||||
DataRetentionSettings: *baseDataRetentionSettings,
|
DataRetentionSettings: *baseDataRetentionSettings,
|
||||||
|
TeamSettings: *baseTeamSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Test Load Plugin Success", func(t *testing.T) {
|
t.Run("Test Load Plugin Success", func(t *testing.T) {
|
||||||
|
@ -261,6 +261,7 @@ func (p *Plugin) createBoardsConfig(mmconfig mmModel.Config, baseURL string, ser
|
|||||||
NotifyFreqBoardSeconds: getPluginSettingInt(mmconfig, notifyFreqBoardSecondsKey, 86400),
|
NotifyFreqBoardSeconds: getPluginSettingInt(mmconfig, notifyFreqBoardSecondsKey, 86400),
|
||||||
EnableDataRetention: enableBoardsDeletion,
|
EnableDataRetention: enableBoardsDeletion,
|
||||||
DataRetentionDays: *mmconfig.DataRetentionSettings.BoardsRetentionDays,
|
DataRetentionDays: *mmconfig.DataRetentionSettings.BoardsRetentionDays,
|
||||||
|
TeammateNameDisplay: *mmconfig.TeamSettings.TeammateNameDisplay,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,10 @@ func TestSetConfiguration(t *testing.T) {
|
|||||||
baseDataRetentionSettings := &model.DataRetentionSettings{
|
baseDataRetentionSettings := &model.DataRetentionSettings{
|
||||||
BoardsRetentionDays: &days,
|
BoardsRetentionDays: &days,
|
||||||
}
|
}
|
||||||
|
usernameRef := "username"
|
||||||
|
baseTeamSettings := &model.TeamSettings{
|
||||||
|
TeammateNameDisplay: &usernameRef,
|
||||||
|
}
|
||||||
|
|
||||||
baseConfig := &model.Config{
|
baseConfig := &model.Config{
|
||||||
FeatureFlags: baseFeatureFlags,
|
FeatureFlags: baseFeatureFlags,
|
||||||
@ -75,6 +79,7 @@ func TestSetConfiguration(t *testing.T) {
|
|||||||
SqlSettings: *baseSQLSettings,
|
SqlSettings: *baseSQLSettings,
|
||||||
FileSettings: *baseFileSettings,
|
FileSettings: *baseFileSettings,
|
||||||
DataRetentionSettings: *baseDataRetentionSettings,
|
DataRetentionSettings: *baseDataRetentionSettings,
|
||||||
|
TeamSettings: *baseTeamSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("test enable telemetry", func(t *testing.T) {
|
t.Run("test enable telemetry", func(t *testing.T) {
|
||||||
|
@ -12,6 +12,8 @@ import {GlobalState} from 'mattermost-redux/types/store'
|
|||||||
import {selectTeam} from 'mattermost-redux/actions/teams'
|
import {selectTeam} from 'mattermost-redux/actions/teams'
|
||||||
|
|
||||||
import {SuiteWindow} from '../../../webapp/src/types/index'
|
import {SuiteWindow} from '../../../webapp/src/types/index'
|
||||||
|
import {UserSettings} from '../../../webapp/src/userSettings'
|
||||||
|
|
||||||
|
|
||||||
const windowAny = (window as SuiteWindow)
|
const windowAny = (window as SuiteWindow)
|
||||||
windowAny.baseURL = '/plugins/focalboard'
|
windowAny.baseURL = '/plugins/focalboard'
|
||||||
@ -182,6 +184,7 @@ export default class Plugin {
|
|||||||
|
|
||||||
this.registry = registry
|
this.registry = registry
|
||||||
|
|
||||||
|
UserSettings.nameFormat = mmStore.getState().entities.preferences?.myPreferences['display_settings--name_format']?.value || null
|
||||||
let theme = mmStore.getState().entities.preferences.myPreferences.theme
|
let theme = mmStore.getState().entities.preferences.myPreferences.theme
|
||||||
setMattermostTheme(theme)
|
setMattermostTheme(theme)
|
||||||
let lastViewedChannel = mmStore.getState().entities.channels.currentChannelId
|
let lastViewedChannel = mmStore.getState().entities.channels.currentChannelId
|
||||||
@ -326,6 +329,9 @@ export default class Plugin {
|
|||||||
setMattermostTheme(JSON.parse(preference.value))
|
setMattermostTheme(JSON.parse(preference.value))
|
||||||
theme = preference.value
|
theme = preference.value
|
||||||
}
|
}
|
||||||
|
if(preference.category === 'display_settings' && preference.name === 'name_format'){
|
||||||
|
UserSettings.nameFormat = preference.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1100,7 +1100,6 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonBytesResponse(w, http.StatusOK, userData)
|
jsonBytesResponse(w, http.StatusOK, userData)
|
||||||
|
|
||||||
auditRec.AddMeta("userID", user.ID)
|
auditRec.AddMeta("userID", user.ID)
|
||||||
|
@ -9,6 +9,7 @@ func (a *App) GetClientConfig() *model.ClientConfig {
|
|||||||
Telemetry: a.config.Telemetry,
|
Telemetry: a.config.Telemetry,
|
||||||
TelemetryID: a.config.TelemetryID,
|
TelemetryID: a.config.TelemetryID,
|
||||||
EnablePublicSharedBoards: a.config.EnablePublicSharedBoards,
|
EnablePublicSharedBoards: a.config.EnablePublicSharedBoards,
|
||||||
|
TeammateNameDisplay: a.config.TeammateNameDisplay,
|
||||||
FeatureFlags: a.config.FeatureFlags,
|
FeatureFlags: a.config.FeatureFlags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ func TestGetClientConfig(t *testing.T) {
|
|||||||
newConfiguration.FeatureFlags = make(map[string]string)
|
newConfiguration.FeatureFlags = make(map[string]string)
|
||||||
newConfiguration.FeatureFlags["BoardsFeature1"] = "true"
|
newConfiguration.FeatureFlags["BoardsFeature1"] = "true"
|
||||||
newConfiguration.FeatureFlags["BoardsFeature2"] = "true"
|
newConfiguration.FeatureFlags["BoardsFeature2"] = "true"
|
||||||
|
newConfiguration.TeammateNameDisplay = "username"
|
||||||
th.App.SetConfig(&newConfiguration)
|
th.App.SetConfig(&newConfiguration)
|
||||||
|
|
||||||
clientConfig := th.App.GetClientConfig()
|
clientConfig := th.App.GetClientConfig()
|
||||||
@ -26,5 +27,6 @@ func TestGetClientConfig(t *testing.T) {
|
|||||||
require.True(t, clientConfig.Telemetry)
|
require.True(t, clientConfig.Telemetry)
|
||||||
require.Equal(t, "abcde", clientConfig.TelemetryID)
|
require.Equal(t, "abcde", clientConfig.TelemetryID)
|
||||||
require.Equal(t, 2, len(clientConfig.FeatureFlags))
|
require.Equal(t, 2, len(clientConfig.FeatureFlags))
|
||||||
|
require.Equal(t, "username", clientConfig.TeammateNameDisplay)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,10 @@ type ClientConfig struct {
|
|||||||
// required: true
|
// required: true
|
||||||
EnablePublicSharedBoards bool `json:"enablePublicSharedBoards"`
|
EnablePublicSharedBoards bool `json:"enablePublicSharedBoards"`
|
||||||
|
|
||||||
|
// Is public shared boards enabled
|
||||||
|
// required: true
|
||||||
|
TeammateNameDisplay string `json:"teammateNameDisplay"`
|
||||||
|
|
||||||
// The server feature flags
|
// The server feature flags
|
||||||
// required: true
|
// required: true
|
||||||
FeatureFlags map[string]string `json:"featureFlags"`
|
FeatureFlags map[string]string `json:"featureFlags"`
|
||||||
|
@ -26,6 +26,13 @@ type User struct {
|
|||||||
// required: true
|
// required: true
|
||||||
Email string `json:"-"`
|
Email string `json:"-"`
|
||||||
|
|
||||||
|
// The user's nickname
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
// The user's first name
|
||||||
|
FirstName string `json:"firstname"`
|
||||||
|
// The user's last name
|
||||||
|
LastName string `json:"lastname"`
|
||||||
|
|
||||||
// swagger:ignore
|
// swagger:ignore
|
||||||
Password string `json:"-"`
|
Password string `json:"-"`
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ type Configuration struct {
|
|||||||
FeatureFlags map[string]string `json:"featureFlags" mapstructure:"featureFlags"`
|
FeatureFlags map[string]string `json:"featureFlags" mapstructure:"featureFlags"`
|
||||||
EnableDataRetention bool `json:"enable_data_retention" mapstructure:"enable_data_retention"`
|
EnableDataRetention bool `json:"enable_data_retention" mapstructure:"enable_data_retention"`
|
||||||
DataRetentionDays int `json:"data_retention_days" mapstructure:"data_retention_days"`
|
DataRetentionDays int `json:"data_retention_days" mapstructure:"data_retention_days"`
|
||||||
|
TeammateNameDisplay string `json:"teammate_name_display" mapstructure:"teammateNameDisplay"`
|
||||||
|
|
||||||
AuthMode string `json:"authMode" mapstructure:"authMode"`
|
AuthMode string `json:"authMode" mapstructure:"authMode"`
|
||||||
|
|
||||||
@ -100,6 +101,7 @@ func ReadConfigFile(configFilePath string) (*Configuration, error) {
|
|||||||
viper.SetDefault("EnableDataRetention", false)
|
viper.SetDefault("EnableDataRetention", false)
|
||||||
viper.SetDefault("DataRetentionDays", 365) // 1 year is default
|
viper.SetDefault("DataRetentionDays", 365) // 1 year is default
|
||||||
viper.SetDefault("PrometheusAddress", "")
|
viper.SetDefault("PrometheusAddress", "")
|
||||||
|
viper.SetDefault("TeammateNameDisplay", "username")
|
||||||
|
|
||||||
err := viper.ReadInConfig() // Find and read the config file
|
err := viper.ReadInConfig() // Find and read the config file
|
||||||
if err != nil { // Handle errors reading the config file
|
if err != nil { // Handle errors reading the config file
|
||||||
|
@ -266,7 +266,7 @@ func (s *MattermostAuthLayer) getQueryBuilder() sq.StatementBuilderType {
|
|||||||
|
|
||||||
func (s *MattermostAuthLayer) GetUsersByTeam(teamID string) ([]*model.User, error) {
|
func (s *MattermostAuthLayer) GetUsersByTeam(teamID string) ([]*model.User, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("u.id", "u.username", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
||||||
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot").
|
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot").
|
||||||
From("Users as u").
|
From("Users as u").
|
||||||
Join("TeamMembers as tm ON tm.UserID = u.ID").
|
Join("TeamMembers as tm ON tm.UserID = u.ID").
|
||||||
@ -291,7 +291,7 @@ func (s *MattermostAuthLayer) GetUsersByTeam(teamID string) ([]*model.User, erro
|
|||||||
|
|
||||||
func (s *MattermostAuthLayer) SearchUsersByTeam(teamID string, searchQuery string) ([]*model.User, error) {
|
func (s *MattermostAuthLayer) SearchUsersByTeam(teamID string, searchQuery string) ([]*model.User, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("u.id", "u.username", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
Select("u.id", "u.username", "u.email", "u.nickname", "u.firstname", "u.lastname", "u.props", "u.CreateAt as create_at", "u.UpdateAt as update_at",
|
||||||
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot").
|
"u.DeleteAt as delete_at", "b.UserId IS NOT NULL AS is_bot").
|
||||||
From("Users as u").
|
From("Users as u").
|
||||||
Join("TeamMembers as tm ON tm.UserID = u.id").
|
Join("TeamMembers as tm ON tm.UserID = u.id").
|
||||||
@ -332,6 +332,10 @@ func (s *MattermostAuthLayer) usersFromRows(rows *sql.Rows) ([]*model.User, erro
|
|||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&user.ID,
|
&user.ID,
|
||||||
&user.Username,
|
&user.Username,
|
||||||
|
&user.Email,
|
||||||
|
&user.Nickname,
|
||||||
|
&user.FirstName,
|
||||||
|
&user.LastName,
|
||||||
&propsBytes,
|
&propsBytes,
|
||||||
&user.CreateAt,
|
&user.CreateAt,
|
||||||
&user.UpdateAt,
|
&user.UpdateAt,
|
||||||
@ -385,6 +389,9 @@ func mmUserToFbUser(mmUser *mmModel.User) model.User {
|
|||||||
Username: mmUser.Username,
|
Username: mmUser.Username,
|
||||||
Email: mmUser.Email,
|
Email: mmUser.Email,
|
||||||
Password: mmUser.Password,
|
Password: mmUser.Password,
|
||||||
|
Nickname: mmUser.Nickname,
|
||||||
|
FirstName: mmUser.FirstName,
|
||||||
|
LastName: mmUser.LastName,
|
||||||
MfaSecret: mmUser.MfaSecret,
|
MfaSecret: mmUser.MfaSecret,
|
||||||
AuthService: mmUser.AuthService,
|
AuthService: mmUser.AuthService,
|
||||||
AuthData: authData,
|
AuthData: authData,
|
||||||
|
@ -61,6 +61,9 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: 0,
|
update_at: 0,
|
||||||
|
@ -96,12 +96,15 @@ describe('components/boardTemplateSelector/boardTemplateSelectorItem', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const me: IUser = {
|
const me: IUser = {
|
||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
nickname: '',
|
||||||
props: {},
|
firstname: '',
|
||||||
create_at: 0,
|
lastname: '',
|
||||||
update_at: 0,
|
email: '',
|
||||||
|
props: {},
|
||||||
|
create_at: 0,
|
||||||
|
update_at: 0,
|
||||||
is_bot: false,
|
is_bot: false,
|
||||||
roles: 'system_user',
|
roles: 'system_user',
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,10 @@ describe('components/content/TextElement', () => {
|
|||||||
boards: {
|
boards: {
|
||||||
[board1.id]: board1,
|
[board1.id]: board1,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
|
|
||||||
|
@ -83,7 +83,10 @@ describe('components/contentBlock', () => {
|
|||||||
boards: {
|
boards: {
|
||||||
[board1.id]: board1,
|
[board1.id]: board1,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
|
|
||||||
|
@ -37,7 +37,10 @@ describe('components/markdownEditor', () => {
|
|||||||
boards: {
|
boards: {
|
||||||
[board1.id]: board1,
|
[board1.id]: board1,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
test('should match snapshot', async () => {
|
test('should match snapshot', async () => {
|
||||||
|
@ -27,6 +27,9 @@ const Entry = (props: EntryComponentProps): ReactElement => {
|
|||||||
{mention.name}
|
{mention.name}
|
||||||
{BotBadge && <BotBadge show={mention.is_bot}/>}
|
{BotBadge && <BotBadge show={mention.is_bot}/>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className={theme?.mentionSuggestionsEntryText}>
|
||||||
|
{mention.displayName}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -21,9 +21,13 @@ import createLiveMarkdownPlugin from '../live-markdown-plugin/liveMarkdownPlugin
|
|||||||
|
|
||||||
import './markdownEditorInput.scss'
|
import './markdownEditorInput.scss'
|
||||||
|
|
||||||
import {BoardTypeOpen} from "../../blocks/board"
|
import {BoardTypeOpen} from '../../blocks/board'
|
||||||
import {getCurrentBoard} from "../../store/boards"
|
import {getCurrentBoard} from '../../store/boards'
|
||||||
import octoClient from "../../octoClient"
|
import octoClient from '../../octoClient'
|
||||||
|
|
||||||
|
import {Utils} from '../../utils'
|
||||||
|
import {ClientConfig} from '../../config/clientConfig'
|
||||||
|
import {getClientConfig} from '../../store/clientConfig'
|
||||||
|
|
||||||
import Entry from './entryComponent/entryComponent'
|
import Entry from './entryComponent/entryComponent'
|
||||||
|
|
||||||
@ -33,6 +37,7 @@ type MentionUser = {
|
|||||||
name: string
|
name: string
|
||||||
avatar: string
|
avatar: string
|
||||||
is_bot: boolean
|
is_bot: boolean
|
||||||
|
displayName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -48,6 +53,7 @@ const MarkdownEditorInput = (props: Props): ReactElement => {
|
|||||||
const {onChange, onFocus, onBlur, initialText, id, isEditing} = props
|
const {onChange, onFocus, onBlur, initialText, id, isEditing} = props
|
||||||
const boardUsers = useAppSelector<IUser[]>(getBoardUsersList)
|
const boardUsers = useAppSelector<IUser[]>(getBoardUsersList)
|
||||||
const board = useAppSelector(getCurrentBoard)
|
const board = useAppSelector(getCurrentBoard)
|
||||||
|
const clientConfig = useAppSelector<ClientConfig>(getClientConfig)
|
||||||
const ref = useRef<Editor>(null)
|
const ref = useRef<Editor>(null)
|
||||||
|
|
||||||
const [suggestions, setSuggestions] = useState<Array<MentionUser>>([])
|
const [suggestions, setSuggestions] = useState<Array<MentionUser>>([])
|
||||||
@ -65,7 +71,8 @@ const MarkdownEditorInput = (props: Props): ReactElement => {
|
|||||||
(user) => ({
|
(user) => ({
|
||||||
name: user.username,
|
name: user.username,
|
||||||
avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`,
|
avatar: `${imageURLForUser ? imageURLForUser(user.id) : ''}`,
|
||||||
is_bot: user.is_bot}
|
is_bot: user.is_bot,
|
||||||
|
displayName: Utils.getUserDisplayName(user, clientConfig.teammateNameDisplay)}
|
||||||
))
|
))
|
||||||
setSuggestions(mentions)
|
setSuggestions(mentions)
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,9 @@ describe('components/messages/CloudMessage', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: 0,
|
update_at: 0,
|
||||||
@ -70,6 +73,9 @@ describe('components/messages/CloudMessage', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {
|
props: {
|
||||||
focalboard_cloudMessageCanceled: 'true',
|
focalboard_cloudMessageCanceled: 'true',
|
||||||
},
|
},
|
||||||
@ -101,6 +107,9 @@ describe('components/messages/CloudMessage', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: 0,
|
update_at: 0,
|
||||||
@ -138,6 +147,9 @@ describe('components/messages/CloudMessage', () => {
|
|||||||
id: 'single-user',
|
id: 'single-user',
|
||||||
username: 'single-user',
|
username: 'single-user',
|
||||||
email: 'single-user',
|
email: 'single-user',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: Date.now() - (1000 * 60 * 60 * 24), //24 hours,
|
update_at: Date.now() - (1000 * 60 * 60 * 24), //24 hours,
|
||||||
|
@ -24,6 +24,11 @@ describe('components/properties/createdBy', () => {
|
|||||||
'user-id-1': {username: 'username_1'} as IUser,
|
'user-id-1': {username: 'username_1'} as IUser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
|
@ -42,6 +42,11 @@ describe('components/properties/lastModifiedBy', () => {
|
|||||||
[card.id]: [comment],
|
[card.id]: [comment],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
|
@ -32,6 +32,11 @@ describe('components/properties/user', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
test('not readonly not existing user', async () => {
|
test('not readonly not existing user', async () => {
|
||||||
@ -110,8 +115,7 @@ describe('components/properties/user', () => {
|
|||||||
<UserProperty
|
<UserProperty
|
||||||
value={'user-id-1'}
|
value={'user-id-1'}
|
||||||
readonly={false}
|
readonly={false}
|
||||||
onChange={() => {
|
onChange={() => {}}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</ReduxProvider>,
|
</ReduxProvider>,
|
||||||
)
|
)
|
||||||
|
@ -5,12 +5,17 @@ import React from 'react'
|
|||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import {CSSObject} from '@emotion/serialize'
|
import {CSSObject} from '@emotion/serialize'
|
||||||
|
|
||||||
|
import {Utils} from '../../../utils'
|
||||||
|
|
||||||
import {IUser} from '../../../user'
|
import {IUser} from '../../../user'
|
||||||
|
|
||||||
import {getBoardUsersList, getBoardUsers} from '../../../store/users'
|
import {getBoardUsersList, getBoardUsers} from '../../../store/users'
|
||||||
import {useAppSelector} from '../../../store/hooks'
|
import {useAppSelector} from '../../../store/hooks'
|
||||||
|
|
||||||
import './user.scss'
|
import './user.scss'
|
||||||
import {getSelectBaseStyle} from '../../../theme'
|
import {getSelectBaseStyle} from '../../../theme'
|
||||||
|
import {ClientConfig} from '../../../config/clientConfig'
|
||||||
|
import {getClientConfig} from '../../../store/clientConfig'
|
||||||
import {propertyValueClassName} from '../../propertyValueUtils'
|
import {propertyValueClassName} from '../../propertyValueUtils'
|
||||||
|
|
||||||
const imageURLForUser = (window as any).Components?.imageURLForUser
|
const imageURLForUser = (window as any).Components?.imageURLForUser
|
||||||
@ -53,26 +58,28 @@ const selectStyles = {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatOptionLabel = (user: any) => {
|
const UserProperty = (props: Props): JSX.Element => {
|
||||||
let profileImg
|
const clientConfig = useAppSelector<ClientConfig>(getClientConfig)
|
||||||
if (imageURLForUser) {
|
|
||||||
profileImg = imageURLForUser(user.id)
|
const formatOptionLabel = (user: any) => {
|
||||||
|
let profileImg
|
||||||
|
if (imageURLForUser) {
|
||||||
|
profileImg = imageURLForUser(user.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='UserProperty-item'>
|
||||||
|
{profileImg && (
|
||||||
|
<img
|
||||||
|
alt='UserProperty-avatar'
|
||||||
|
src={profileImg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{Utils.getUserDisplayName(user, clientConfig.teammateNameDisplay)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='UserProperty-item'>
|
|
||||||
{profileImg && (
|
|
||||||
<img
|
|
||||||
alt='UserProperty-avatar'
|
|
||||||
src={profileImg}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{user.username}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserProperty = (props: Props): JSX.Element => {
|
|
||||||
const boardUsersById = useAppSelector<{[key:string]: IUser}>(getBoardUsers)
|
const boardUsersById = useAppSelector<{[key:string]: IUser}>(getBoardUsers)
|
||||||
|
|
||||||
const user = boardUsersById[props.value]
|
const user = boardUsersById[props.value]
|
||||||
|
@ -101,6 +101,9 @@ const me: IUser = {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: 0,
|
update_at: 0,
|
||||||
@ -157,6 +160,7 @@ describe('src/components/shareBoard/shareBoard', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -479,6 +483,7 @@ describe('src/components/shareBoard/shareBoard', () => {
|
|||||||
}
|
}
|
||||||
mockedOctoClient.getSharing.mockResolvedValue(sharing)
|
mockedOctoClient.getSharing.mockResolvedValue(sharing)
|
||||||
mockedUtils.isFocalboardPlugin.mockReturnValue(true)
|
mockedUtils.isFocalboardPlugin.mockReturnValue(true)
|
||||||
|
mockedUtils.getUserDisplayName.mockImplementation((u) => u.username)
|
||||||
|
|
||||||
const users:IUser[] = [
|
const users:IUser[] = [
|
||||||
{id: 'userid1', username: 'username_1'} as IUser,
|
{id: 'userid1', username: 'username_1'} as IUser,
|
||||||
|
@ -13,6 +13,9 @@ import {getCurrentBoard, getCurrentBoardMembers} from '../../store/boards'
|
|||||||
import {Channel, ChannelTypeOpen, ChannelTypePrivate} from '../../store/channels'
|
import {Channel, ChannelTypeOpen, ChannelTypePrivate} from '../../store/channels'
|
||||||
import {getMe, getBoardUsersList} from '../../store/users'
|
import {getMe, getBoardUsersList} from '../../store/users'
|
||||||
|
|
||||||
|
import {ClientConfig} from '../../config/clientConfig'
|
||||||
|
import {getClientConfig} from '../../store/clientConfig'
|
||||||
|
|
||||||
import {Utils, IDType} from '../../utils'
|
import {Utils, IDType} from '../../utils'
|
||||||
import Tooltip from '../../widgets/tooltip'
|
import Tooltip from '../../widgets/tooltip'
|
||||||
import mutator from '../../mutator'
|
import mutator from '../../mutator'
|
||||||
@ -100,6 +103,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element {
|
|||||||
const [showLinkChannelConfirmation, setShowLinkChannelConfirmation] = useState<Channel|null>(null)
|
const [showLinkChannelConfirmation, setShowLinkChannelConfirmation] = useState<Channel|null>(null)
|
||||||
const [sharing, setSharing] = useState<ISharing|undefined>(undefined)
|
const [sharing, setSharing] = useState<ISharing|undefined>(undefined)
|
||||||
const [selectedUser, setSelectedUser] = useState<IUser|Channel|null>(null)
|
const [selectedUser, setSelectedUser] = useState<IUser|Channel|null>(null)
|
||||||
|
const clientConfig = useAppSelector<ClientConfig>(getClientConfig)
|
||||||
|
|
||||||
// members of the current board
|
// members of the current board
|
||||||
const members = useAppSelector<{[key: string]: BoardMember}>(getCurrentBoardMembers)
|
const members = useAppSelector<{[key: string]: BoardMember}>(getCurrentBoardMembers)
|
||||||
@ -293,7 +297,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<div className='ml-3'>
|
<div className='ml-3'>
|
||||||
<strong>{user.username}</strong>
|
<strong>{Utils.getUserDisplayName(user, clientConfig.teammateNameDisplay)}</strong>
|
||||||
<strong className='ml-2 text-light'>{`@${user.username}`}</strong>
|
<strong className='ml-2 text-light'>{`@${user.username}`}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -390,6 +394,7 @@ export default function ShareBoardDialog(props: Props): JSX.Element {
|
|||||||
key={user.id}
|
key={user.id}
|
||||||
user={user}
|
user={user}
|
||||||
member={members[user.id]}
|
member={members[user.id]}
|
||||||
|
teammateNameDisplay={me?.props?.teammateNameDisplay || clientConfig.teammateNameDisplay}
|
||||||
onDeleteBoardMember={onDeleteBoardMember}
|
onDeleteBoardMember={onDeleteBoardMember}
|
||||||
onUpdateBoardMember={onUpdateBoardMember}
|
onUpdateBoardMember={onUpdateBoardMember}
|
||||||
isMe={user.id === me?.id}
|
isMe={user.id === me?.id}
|
||||||
|
@ -21,13 +21,14 @@ type Props = {
|
|||||||
user: IUser
|
user: IUser
|
||||||
member: BoardMember
|
member: BoardMember
|
||||||
isMe: boolean
|
isMe: boolean
|
||||||
|
teammateNameDisplay: string,
|
||||||
onDeleteBoardMember: (member: BoardMember) => void
|
onDeleteBoardMember: (member: BoardMember) => void
|
||||||
onUpdateBoardMember: (member: BoardMember, permission: string) => void
|
onUpdateBoardMember: (member: BoardMember, permission: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserPermissionsRow = (props: Props): JSX.Element => {
|
const UserPermissionsRow = (props: Props): JSX.Element => {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const {user, member, isMe} = props
|
const {user, member, isMe, teammateNameDisplay} = props
|
||||||
let currentRole = 'Viewer'
|
let currentRole = 'Viewer'
|
||||||
if (member.schemeAdmin) {
|
if (member.schemeAdmin) {
|
||||||
currentRole = 'Admin'
|
currentRole = 'Admin'
|
||||||
@ -47,7 +48,7 @@ const UserPermissionsRow = (props: Props): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<div className='ml-3'>
|
<div className='ml-3'>
|
||||||
<strong>{user.username}</strong>
|
<strong>{Utils.getUserDisplayName(user, teammateNameDisplay)}</strong>
|
||||||
<strong className='ml-2 text-light'>{`@${user.username}`}</strong>
|
<strong className='ml-2 text-light'>{`@${user.username}`}</strong>
|
||||||
{isMe && <strong className='ml-2 text-light'>{intl.formatMessage({id: 'ShareBoard.userPermissionsYouText', defaultMessage: '(You)'})}</strong>}
|
{isMe && <strong className='ml-2 text-light'>{intl.formatMessage({id: 'ShareBoard.userPermissionsYouText', defaultMessage: '(You)'})}</strong>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`components/table/Table extended should match snapshot with CreatedBy 1`] = `
|
exports[`components/table/Table extended should match snapshot with CreatedAt 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="Table"
|
class="Table"
|
||||||
@ -460,7 +460,7 @@ exports[`components/table/Table extended should match snapshot with CreatedBy 1`
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/table/Table extended should match snapshot with CreatedBy 2`] = `
|
exports[`components/table/Table extended should match snapshot with CreatedBy 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="Table"
|
class="Table"
|
||||||
|
@ -301,9 +301,14 @@ describe('components/table/Table extended', () => {
|
|||||||
board_id: {userId: 'user_id_1', schemeAdmin: true},
|
board_id: {userId: 'user_id_1', schemeAdmin: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should match snapshot with CreatedBy', async () => {
|
test('should match snapshot with CreatedAt', async () => {
|
||||||
const board = TestBlockFactory.createBoard()
|
const board = TestBlockFactory.createBoard()
|
||||||
|
|
||||||
const dateCreatedId = Utils.createGuid(IDType.User)
|
const dateCreatedId = Utils.createGuid(IDType.User)
|
||||||
|
@ -53,6 +53,9 @@ describe('components/viewTitle', () => {
|
|||||||
[board.id]: {userId: 'user_id_1', schemeAdmin: true},
|
[board.id]: {userId: 'user_id_1', schemeAdmin: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
clientConfig: {
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
|
|
||||||
|
@ -76,6 +76,9 @@ const me: IUser = {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: 0,
|
create_at: 0,
|
||||||
update_at: 0,
|
update_at: 0,
|
||||||
@ -150,6 +153,7 @@ describe('src/components/workspace', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -272,6 +276,7 @@ describe('src/components/workspace', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -310,6 +315,9 @@ describe('src/components/workspace', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {
|
props: {
|
||||||
focalboard_welcomePageViewed: '1',
|
focalboard_welcomePageViewed: '1',
|
||||||
focalboard_onboardingTourStarted: true,
|
focalboard_onboardingTourStarted: true,
|
||||||
@ -361,6 +369,7 @@ describe('src/components/workspace', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -410,6 +419,9 @@ describe('src/components/workspace', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {
|
props: {
|
||||||
focalboard_welcomePageViewed: '1',
|
focalboard_welcomePageViewed: '1',
|
||||||
focalboard_onboardingTourStarted: true,
|
focalboard_onboardingTourStarted: true,
|
||||||
@ -461,6 +473,7 @@ describe('src/components/workspace', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -515,6 +528,9 @@ describe('src/components/workspace', () => {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'username_1',
|
username: 'username_1',
|
||||||
email: '',
|
email: '',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {
|
props: {
|
||||||
focalboard_welcomePageViewed: '1',
|
focalboard_welcomePageViewed: '1',
|
||||||
focalboard_onboardingTourStarted: true,
|
focalboard_onboardingTourStarted: true,
|
||||||
@ -566,6 +582,7 @@ describe('src/components/workspace', () => {
|
|||||||
telemetry: true,
|
telemetry: true,
|
||||||
telemetryid: 'telemetry',
|
telemetryid: 'telemetry',
|
||||||
enablePublicSharedBoards: true,
|
enablePublicSharedBoards: true,
|
||||||
|
teammateNameDisplay: 'username',
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6,4 +6,5 @@ export type ClientConfig = {
|
|||||||
telemetryid: string,
|
telemetryid: string,
|
||||||
enablePublicSharedBoards: boolean,
|
enablePublicSharedBoards: boolean,
|
||||||
featureFlags: Record<string, string>,
|
featureFlags: Record<string, string>,
|
||||||
|
teammateNameDisplay: string,
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import {ClientConfig} from '../config/clientConfig'
|
|||||||
|
|
||||||
import {default as client} from '../octoClient'
|
import {default as client} from '../octoClient'
|
||||||
|
|
||||||
|
import {ShowUsername} from '../utils'
|
||||||
|
|
||||||
import {RootState} from './index'
|
import {RootState} from './index'
|
||||||
|
|
||||||
export const fetchClientConfig = createAsyncThunk(
|
export const fetchClientConfig = createAsyncThunk(
|
||||||
@ -16,7 +18,7 @@ export const fetchClientConfig = createAsyncThunk(
|
|||||||
|
|
||||||
const clientConfigSlice = createSlice({
|
const clientConfigSlice = createSlice({
|
||||||
name: 'config',
|
name: 'config',
|
||||||
initialState: {value: {telemetry: false, telemetryid: '', enablePublicSharedBoards: false, featureFlags: {}}} as {value: ClientConfig},
|
initialState: {value: {telemetry: false, telemetryid: '', enablePublicSharedBoards: false, teammateNameDisplay: ShowUsername, featureFlags: {}}} as {value: ClientConfig},
|
||||||
reducers: {
|
reducers: {
|
||||||
setClientConfig: (state, action: PayloadAction<ClientConfig>) => {
|
setClientConfig: (state, action: PayloadAction<ClientConfig>) => {
|
||||||
state.value = action.payload
|
state.value = action.payload
|
||||||
@ -24,7 +26,7 @@ const clientConfigSlice = createSlice({
|
|||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(fetchClientConfig.fulfilled, (state, action) => {
|
builder.addCase(fetchClientConfig.fulfilled, (state, action) => {
|
||||||
state.value = action.payload || {telemetry: false, telemetryid: '', enablePublicSharedBoards: false, featureFlags: {}}
|
state.value = action.payload || {telemetry: false, telemetryid: '', enablePublicSharedBoards: false, teammateNameDisplay: ShowUsername, featureFlags: {}}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -35,4 +37,3 @@ export const {reducer} = clientConfigSlice
|
|||||||
export function getClientConfig(state: RootState): ClientConfig {
|
export function getClientConfig(state: RootState): ClientConfig {
|
||||||
return state.clientConfig.value
|
return state.clientConfig.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +187,9 @@ class TestBlockFactory {
|
|||||||
id: 'user-id-1',
|
id: 'user-id-1',
|
||||||
username: 'Dwight Schrute',
|
username: 'Dwight Schrute',
|
||||||
email: 'dwight.schrute@dundermifflin.com',
|
email: 'dwight.schrute@dundermifflin.com',
|
||||||
|
nickname: '',
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
props: {},
|
props: {},
|
||||||
create_at: Date.now(),
|
create_at: Date.now(),
|
||||||
update_at: Date.now(),
|
update_at: Date.now(),
|
||||||
|
@ -5,6 +5,9 @@ interface IUser {
|
|||||||
id: string,
|
id: string,
|
||||||
username: string,
|
username: string,
|
||||||
email: string,
|
email: string,
|
||||||
|
nickname: string,
|
||||||
|
firstname: string,
|
||||||
|
lastname: string,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
props: Record<string, any>,
|
props: Record<string, any>,
|
||||||
create_at: number,
|
create_at: number,
|
||||||
|
@ -17,7 +17,8 @@ export enum UserSettingKey {
|
|||||||
RandomIcons = 'randomIcons',
|
RandomIcons = 'randomIcons',
|
||||||
MobileWarningClosed = 'mobileWarningClosed',
|
MobileWarningClosed = 'mobileWarningClosed',
|
||||||
WelcomePageViewed = 'welcomePageViewed',
|
WelcomePageViewed = 'welcomePageViewed',
|
||||||
HideCloudMessage = 'hideCloudMessage'
|
HideCloudMessage = 'hideCloudMessage',
|
||||||
|
NameFormat = 'nameFormat'
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserSettings {
|
export class UserSettings {
|
||||||
@ -155,6 +156,15 @@ export class UserSettings {
|
|||||||
static set hideCloudMessage(newValue: boolean) {
|
static set hideCloudMessage(newValue: boolean) {
|
||||||
localStorage.setItem(UserSettingKey.HideCloudMessage, JSON.stringify(newValue))
|
localStorage.setItem(UserSettingKey.HideCloudMessage, JSON.stringify(newValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get nameFormat(): string | null {
|
||||||
|
return UserSettings.get(UserSettingKey.NameFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
static set nameFormat(newValue: string | null) {
|
||||||
|
UserSettings.set(UserSettingKey.NameFormat, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportUserSettingsBlob(): string {
|
export function exportUserSettingsBlob(): string {
|
||||||
|
@ -7,7 +7,9 @@ import {createMemoryHistory} from "history"
|
|||||||
|
|
||||||
import {match as routerMatch} from "react-router-dom"
|
import {match as routerMatch} from "react-router-dom"
|
||||||
|
|
||||||
import {Utils, IDType} from './utils'
|
import {Utils, IDType, ShowFullName, ShowNicknameFullName, ShowUsername} from './utils'
|
||||||
|
import {IUser} from './user'
|
||||||
|
|
||||||
import {IAppWindow} from './types'
|
import {IAppWindow} from './types'
|
||||||
|
|
||||||
declare let window: IAppWindow
|
declare let window: IAppWindow
|
||||||
@ -186,4 +188,49 @@ describe('utils', () => {
|
|||||||
expect(history.push).toBeCalledWith('/team/team_id_1/board_id_2')
|
expect(history.push).toBeCalledWith('/team/team_id_1/board_id_2')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('getUserDisplayName test', () => {
|
||||||
|
const user: IUser = {
|
||||||
|
id: 'user-id-1',
|
||||||
|
username: 'username_1',
|
||||||
|
email: 'test@email.com',
|
||||||
|
nickname: 'nickname',
|
||||||
|
firstname: 'firstname',
|
||||||
|
lastname: 'lastname',
|
||||||
|
props: {},
|
||||||
|
create_at: 0,
|
||||||
|
update_at: 0,
|
||||||
|
is_bot: false,
|
||||||
|
roles: 'system_user',
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should display username, by default', () => {
|
||||||
|
const displayName = Utils.getUserDisplayName(user, '')
|
||||||
|
expect(displayName).toEqual('username_1')
|
||||||
|
})
|
||||||
|
it('should display nickname', () => {
|
||||||
|
const displayName = Utils.getUserDisplayName(user, ShowNicknameFullName)
|
||||||
|
expect(displayName).toEqual('nickname')
|
||||||
|
})
|
||||||
|
it('should display fullname', () => {
|
||||||
|
const displayName = Utils.getUserDisplayName(user, ShowFullName)
|
||||||
|
expect(displayName).toEqual('firstname lastname')
|
||||||
|
})
|
||||||
|
it('should display username', () => {
|
||||||
|
const displayName = Utils.getUserDisplayName(user, ShowUsername)
|
||||||
|
expect(displayName).toEqual('username_1')
|
||||||
|
})
|
||||||
|
it('should display full name, no nickname', () => {
|
||||||
|
user.nickname = ''
|
||||||
|
const displayName = Utils.getUserDisplayName(user, ShowNicknameFullName)
|
||||||
|
expect(displayName).toEqual('firstname lastname')
|
||||||
|
})
|
||||||
|
it('should display username, no nickname, no full name', () => {
|
||||||
|
user.nickname = ''
|
||||||
|
user.firstname = ''
|
||||||
|
user.lastname = ''
|
||||||
|
const displayName = Utils.getUserDisplayName(user, ShowNicknameFullName)
|
||||||
|
expect(displayName).toEqual('username_1')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -8,6 +8,8 @@ import {generatePath, match as routerMatch} from "react-router-dom"
|
|||||||
|
|
||||||
import {History} from "history"
|
import {History} from "history"
|
||||||
|
|
||||||
|
import {IUser} from './user'
|
||||||
|
|
||||||
import {Block} from './blocks/block'
|
import {Block} from './blocks/block'
|
||||||
import {Board as BoardType, BoardMember, createBoard} from './blocks/board'
|
import {Board as BoardType, BoardMember, createBoard} from './blocks/board'
|
||||||
import {createBoardView} from './blocks/boardView'
|
import {createBoardView} from './blocks/boardView'
|
||||||
@ -16,6 +18,7 @@ import {createCommentBlock} from './blocks/commentBlock'
|
|||||||
import {IAppWindow} from './types'
|
import {IAppWindow} from './types'
|
||||||
import {ChangeHandlerType, WSMessage} from './wsclient'
|
import {ChangeHandlerType, WSMessage} from './wsclient'
|
||||||
import {BoardCategoryWebsocketData, Category} from './store/sidebar'
|
import {BoardCategoryWebsocketData, Category} from './store/sidebar'
|
||||||
|
import {UserSettings} from './userSettings'
|
||||||
|
|
||||||
declare let window: IAppWindow
|
declare let window: IAppWindow
|
||||||
|
|
||||||
@ -49,6 +52,10 @@ export const KeyCodes: Record<string, [string, number]> = {
|
|||||||
COMPOSING: ['Composing', 229],
|
COMPOSING: ['Composing', 229],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ShowUsername = 'username'
|
||||||
|
export const ShowNicknameFullName = 'nickname_full_name'
|
||||||
|
export const ShowFullName = 'full_name'
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
static createGuid(idType: IDType): string {
|
static createGuid(idType: IDType): string {
|
||||||
const data = Utils.randomArray(16)
|
const data = Utils.randomArray(16)
|
||||||
@ -80,6 +87,45 @@ class Utils {
|
|||||||
return imageURLForUser && userId ? imageURLForUser(userId) : defaultImageUrl
|
return imageURLForUser && userId ? imageURLForUser(userId) : defaultImageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getUserDisplayName(user: IUser, configNameFormat: string): string {
|
||||||
|
let nameFormat = configNameFormat
|
||||||
|
if(UserSettings.nameFormat){
|
||||||
|
nameFormat=UserSettings.nameFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// default nameFormat = 'username'
|
||||||
|
let displayName = user.username
|
||||||
|
|
||||||
|
if (nameFormat === ShowNicknameFullName) {
|
||||||
|
if( user.nickname != '') {
|
||||||
|
displayName = user.nickname
|
||||||
|
} else {
|
||||||
|
const fullName = Utils.getFullName(user)
|
||||||
|
if(fullName != ''){
|
||||||
|
displayName = fullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nameFormat == ShowFullName) {
|
||||||
|
const fullName = Utils.getFullName(user)
|
||||||
|
if(fullName != ''){
|
||||||
|
displayName = fullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFullName(user: IUser): string {
|
||||||
|
if (user.firstname != '' && user.lastname != '') {
|
||||||
|
return user.firstname + ' ' + user.lastname
|
||||||
|
} else if (user.firstname != '') {
|
||||||
|
return user.firstname
|
||||||
|
} else if (user.lastname != '') {
|
||||||
|
return user.lastname
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static randomArray(size: number): Uint8Array {
|
static randomArray(size: number): Uint8Array {
|
||||||
const crypto = window.crypto || window.msCrypto
|
const crypto = window.crypto || window.msCrypto
|
||||||
const rands = new Uint8Array(size)
|
const rands = new Uint8Array(size)
|
||||||
|
Reference in New Issue
Block a user