Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/Display "<user> has joined <community>" message when unregistered user joins[NEW] #2067

Merged
merged 19 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[unreleased]

* Send an info message immediately after a user joins the community

[2.0.3-alpha.6]

* Fix: filter out invalid peer addresses in peer list. Update peer list in localdb.
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './naming'
export * from './fileData'
export * from './libp2p'
export * from './tests'
export * from './messages'
50 changes: 50 additions & 0 deletions packages/common/src/messages.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { PublicChannelStorage } from '@quiet/types'
import { generateChannelId } from './channelAddress'
import { userCreatedChannelMessage, userJoinedMessage, verifyUserInfoMessage } from './messages'

describe('messages helper', () => {
const username = 'johnny'

const generalChannel: PublicChannelStorage = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally you'd use factory-girl for mocking channel objects. It's minor though.

const generalChannel = (await factory.build<typeof publicChannels.actions.addChannel>('PublicChannel', {
      id: generateChannelId('general'),
      name: 'general',
      owner: username
    })).payload

name: 'general',
description: 'Welcome to #general',
timestamp: 1,
owner: username,
id: generateChannelId('general'),
messages: { ids: [], entities: {} },
}

const sportChannel: PublicChannelStorage = {
name: 'sport',
description: 'Welcome to #sport',
timestamp: 1,
owner: username,
id: generateChannelId('sport'),
messages: { ids: [], entities: {} },
}
it('userCreatedChannelMessage', () => {
const expectedMessage = '@johnny created #sport'
const message = userCreatedChannelMessage(username, sportChannel.name)
expect(message).toEqual(expectedMessage)
})

it('userJoinedMessage', () => {
const expectedMessage =
'**@johnny** has joined and will be registered soon. 🎉 [Learn more](https://github.com/TryQuiet/quiet/wiki/Quiet-FAQ#how-does-username-registration-work)'
const message = userJoinedMessage(username)
expect(message).toEqual(expectedMessage)
})

it('verifyUserInfoMessage - general channel', () => {
const expectedMessage =
'**@johnny** has joined and will be registered soon. 🎉 [Learn more](https://github.com/TryQuiet/quiet/wiki/Quiet-FAQ#how-does-username-registration-work)'
const message = verifyUserInfoMessage(username, generalChannel)
expect(message).toEqual(expectedMessage)
})

it('verifyUserInfoMessage - other channel', () => {
const expectedMessage = '@johnny created #sport'
const message = verifyUserInfoMessage(username, sportChannel)
expect(message).toEqual(expectedMessage)
})
})
17 changes: 17 additions & 0 deletions packages/common/src/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PublicChannelStorage } from '@quiet/types'

export const userCreatedChannelMessage = (username: string, channelName: string) =>
`@${username} created #${channelName}`

export const generalChannelDeletionMessage = (username: string) => `@${username} deleted all messages in #general`

export const userJoinedMessage = (username: string) =>
`**@${username}** has joined and will be registered soon. 🎉 [Learn more](https://github.com/TryQuiet/quiet/wiki/Quiet-FAQ#how-does-username-registration-work)`

export const verifyUserInfoMessage = (username: string, channel: PublicChannelStorage) => {
if (channel.name === 'general') {
return userJoinedMessage(username)
} else {
return userCreatedChannelMessage(username, channel.name)
}
}
11 changes: 7 additions & 4 deletions packages/e2e-tests/src/tests/invitationLink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Sidebar,
WarningModal,
} from '../selectors'
import { capitalizeFirstLetter, composeInvitationDeepUrl, parseInvitationCode } from '@quiet/common'
import { capitalizeFirstLetter, composeInvitationDeepUrl, parseInvitationCode, userJoinedMessage } from '@quiet/common'
import { execSync } from 'child_process'
import { type SupportedPlatformDesktop } from '@quiet/types'

Expand Down Expand Up @@ -184,10 +184,13 @@ describe('New user joins using invitation link while having app opened', () => {
console.log('Invitation Link', 21)
const generalChannel = new Channel(ownerApp.driver, 'general')
await generalChannel.element.isDisplayed()
const userJoinedMessage = await generalChannel.getMessage(
`@${joiningUserUsername} has joined ${capitalizeFirstLetter(communityName)}!`

const hasMessage = await generalChannel.waitForUserMessage(
joiningUserUsername,
userJoinedMessage(joiningUserUsername)
)
expect(await userJoinedMessage.isDisplayed()).toBeTruthy()
const isMessageDisplayed = await hasMessage?.isDisplayed()
expect(isMessageDisplayed).toBeTruthy()
})
})
})
2 changes: 1 addition & 1 deletion packages/e2e-tests/src/tests/multipleClients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ describe('Multiple Clients', () => {
const messages2 = await generalChannelUser1.getUserMessages(users.user1.username)
const messages1 = await generalChannelUser1.getUserMessages(users.owner.username)
console.log({ messages1, messages2 })
const text2 = await messages2[0].getText()
const text2 = await messages2[1].getText()
expect(text2).toEqual(users.user1.messages[0])
})
it('First user opens the settings tab and copies updated invitation code', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ describe('communitiesSelectors', () => {
'MIIDdDCCAxugAwIBAgIGAYeiqwwYMAoGCCqGSM49BAMCMA8xDTALBgNVBAMTBHRlc3QwHhcNMjMwNDIxMDcxNTMxWhcNMzAwMTMxMjMwMDAwWjBJMUcwRQYDVQQDEz5qYm1zbXR4Z2Rhd241ZTdyZ3Z5ZGZ3bGFuY3c1bnZkcmZjdXdvdmltNzJqeXY2ZTN5eDR0ZXhxZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBjP55M/p8QVQGdgjtAdGwLS8uyzyIaWzvnuCvOwLs/u+FHUdb0DU2+M4TYEZjVHmqn+hSERs4XHG0/tbaaGSyGjggInMIICIzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIAgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwggFHBgkqhkiG9w0BCQwEggE4BIIBNCqfocsbSvEqAdeRObiywx0KV2r7xEnqFysFuc1InEpwF3707TZGrFxww74g/ccxHCZ9zda4EHawgLoU6oKdeyec8W7qAThnWCRzJcOPINdZaTR45g28jeWAXMAtVG6eYtEQS4t7g915QaB0uYUoM3Teqp/qaMhk/9Hs4jiKlN3wL9WFYRf14XQIIVu3Fb0f3sD2/ejNPRJztJeJCwYtcFNF3fhPpH5bSPlcy6IaxhQrMXboqAfSAUlnMD4PifHFxvQYbfvTEC65Gt+FzwJ956BA5PuKsGFf+NVznyp5/YtFrl0XRRdlBcTzp2jreqhxBCdsvCpPwvM2TRv4OPk+hjMPPzBdPgvs5tytiFFyK9hXemai2TTwP1qo+VuV5SYyAyZP4rPxc/XEDHk+W3QN0vF8Ff+iMBUGCisGAQQBg4wbAgEEBxMFYWxpY2UwPQYJKwYBAgEPAwEBBDATLlFtZVN0WFY5VERXVHhoYXZUd25DZWdaYnNvMndQZ3BYQ2lzdHlCTEo2b0MyZHcwSQYDVR0RBEIwQII+amJtc210eGdkYXduNWU3cmd2eWRmd2xhbmN3NW52ZHJmY3V3b3ZpbTcyanl2NmUzeXg0dGV4cWQub25pb24wCgYIKoZIzj0EAwIDRwAwRAIga3etWmNtiMT/SUZkG0Rf5kwl3HxsGDJXsU7X5aCQAvMCIFKVBnCbTPseU5gQwamWZDG9ZoMf0X1VGzYUixWvmzuc'

const factory = await getFactory(store)
store.dispatch(
usersActions.storeUserCertificate({
Kacper-RF marked this conversation as resolved.
Show resolved Hide resolved
certificate: ownerCertificate,
})
)
await factory.create<ReturnType<typeof communitiesActions.addOwnerCertificate>['payload']>('Community', {
ownerCertificate,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ export const ownerNickname = createSelector(
currentCommunity,
getOldestParsedCerificate,
(community, oldestParsedCerificate) => {
if (!oldestParsedCerificate) return undefined
const ownerCertificate = community?.ownerCertificate || undefined

let nickname: string | null
let nickname: string | null = null

if (ownerCertificate) {
const certificate = ownerCertificate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type PayloadAction } from '@reduxjs/toolkit'
import { put, select } from 'typed-redux-saga'
import { type Socket } from '../../../types'
import { publicChannelsActions } from '../../publicChannels/publicChannels.slice'
import { communitiesSelectors } from '../communities.selectors'
import { communitiesActions } from '../communities.slice'

Expand All @@ -22,4 +23,6 @@ export function* saveCommunityMetadataSaga(
ownerCertificate: action.payload.ownerCertificate,
})
)

yield* put(publicChannelsActions.sendUnregisteredInfoMessage())
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { validCurrentPublicChannelMessagesEntries } from './messages.selectors'
import { type communitiesActions } from '../communities/communities.slice'
import { type identityActions } from '../identity/identity.slice'
import { type FactoryGirl } from 'factory-girl'
import { publicChannelsSelectors, selectGeneralChannel } from '../publicChannels/publicChannels.selectors'
import { publicChannelsSelectors } from '../publicChannels/publicChannels.selectors'
import { type Community, type Identity, type PublicChannel, type ChannelMessage } from '@quiet/types'

describe('messagesSelectors', () => {
Expand All @@ -20,7 +20,7 @@ describe('messagesSelectors', () => {
let alice: Identity
let john: Identity

beforeAll(async () => {
beforeEach(async () => {
setupCrypto()

// Set date display format
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createSelector } from 'reselect'
import { channelMessagesAdapter } from '../publicChannels/publicChannels.adapter'
import { currentChannelId } from '../publicChannels/publicChannels.selectors'
import { currentChannelId, generalChannel } from '../publicChannels/publicChannels.selectors'
import { StoreKeys } from '../store.keys'
import { type CreatedSelectors, type StoreState } from '../store.types'
import { allUsers } from '../users/users.selectors'
Expand Down
Loading
Loading