Skip to content

Commit

Permalink
Fix: Not initializing on reconnection when general channel was deleted (
Browse files Browse the repository at this point in the history
#2334) (#2400)

* Fix issue with users joining a community where the general channel was deleted while they were offline

* Update e2e tests to include case for this bug

* Update CHANGELOG.md
  • Loading branch information
Isla Koenigsknecht authored and Lucas Leblow committed Apr 4, 2024
1 parent 29c1c1b commit 3a9529a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Fixes

* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332))
* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334))

# New features

Expand Down
14 changes: 7 additions & 7 deletions packages/backend/src/nest/storage/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,15 +620,15 @@ export class StorageService extends EventEmitter {
}
}
await repo.db.load()
const allEntries = this.getAllEventLogRawEntries(repo.db)
// const allEntries = this.getAllEventLogRawEntries(repo.db)
await repo.db.close()
await repo.db.drop()
const hashes = allEntries.map(e => CID.parse(e.hash))
const files = allEntries
.map(e => {
return e.payload.value.media
})
.filter(isDefined)
// const hashes = allEntries.map(e => CID.parse(e.hash))
// const files = allEntries
// .map(e => {
// return e.payload.value.media
// })
// .filter(isDefined)
// await this.deleteChannelFiles(files)
// await this.deleteChannelMessages(hashes)
this.publicChannelsRepos.delete(channelId)
Expand Down
84 changes: 77 additions & 7 deletions packages/e2e-tests/src/tests/multipleClients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ describe('Multiple Clients', () => {

let secondChannelUser1: Channel

let thirdChannelOwner: Channel

let channelContextMenuOwner: ChannelContextMenu

let invitationCode: string
Expand All @@ -37,6 +39,7 @@ describe('Multiple Clients', () => {
const communityName = 'testcommunity'
const displayedCommunityName = 'Testcommunity'
const newChannelName = 'mid-night-club'
const thirdChannelName = 'delete-this'

const sleep = async (time = 1000) => {
await new Promise<void>(resolve =>
Expand Down Expand Up @@ -342,16 +345,42 @@ describe('Multiple Clients', () => {
const channels = await sidebarOwner.getChannelList()
expect(channels.length).toEqual(2)
})

it('Channel deletion - Owner creates third channel', async () => {
await sidebarOwner.addNewChannel(thirdChannelName)
await sidebarOwner.switchChannel(thirdChannelName)
thirdChannelOwner = new Channel(users.owner.app.driver, thirdChannelName)
const messages = await thirdChannelOwner.getUserMessages(users.owner.username)
expect(messages.length).toEqual(1)
await new Promise<void>(resolve =>
setTimeout(() => {
resolve()
}, 2000)
)
const channels = await sidebarUser1.getChannelList()
expect(channels.length).toEqual(3)
})

// End of tests for Windows
if (process.platform !== 'win32') {
it('Leave community', async () => {
console.log('TEST 2')
const settingsModal = await new Sidebar(users.user1.app.driver).openSettings()
const isSettingsModal = await settingsModal.element.isDisplayed()
expect(isSettingsModal).toBeTruthy()
await settingsModal.openLeaveCommunityModal()
await settingsModal.leaveCommunityButton()
it('User 1 closes app', async () => {
console.log('User 1 closes app')
await users.user1.app?.close()
})

// Delete third channel while guest is absent
it('Channel deletion - Owner deletes third channel', async () => {
console.log('TEST 2.5')
await new Promise<void>(resolve => setTimeout(() => resolve(), 10000))
const isThirdChannel = await thirdChannelOwner.messageInput.isDisplayed()
expect(isThirdChannel).toBeTruthy()
await channelContextMenuOwner.openMenu()
await channelContextMenuOwner.openDeletionChannelModal()
await channelContextMenuOwner.deleteChannel()
const channels = await sidebarOwner.getChannelList()
expect(channels.length).toEqual(2)
})

// Delete general channel while guest is absent
it('Channel deletion - Owner recreates general channel', async () => {
console.log('TEST 3')
Expand All @@ -365,6 +394,47 @@ describe('Multiple Clients', () => {
expect(channels.length).toEqual(2)
})

it('User 1 re-opens app', async () => {
console.log('User 1 re-opens app')
await users.user1.app?.open()
await new Promise<void>(resolve => setTimeout(() => resolve(), 30000))
})

// Check correct channels replication
it('Channel deletion - User sees information about recreation general channel and see correct amount of messages (#2334)', async () => {
console.log('TESTING - ISSUE 2334')
generalChannelUser1 = new Channel(users.user1.app.driver, 'general')
await generalChannelUser1.element.isDisplayed()
console.timeEnd(`[${users.user1.app.name}] '${users.user2.username}' joining community time`)
await new Promise<void>(resolve =>
setTimeout(() => {
resolve()
}, 10000)
)

await generalChannelUser1.waitForUserMessage(
users.owner.username,
`@${users.owner.username} deleted all messages in #general`
)
await generalChannelUser1.waitForUserMessage(
users.owner.username,
`@${users.owner.username} deleted #${thirdChannelName}`
)
await generalChannelUser1.waitForUserMessage(
users.owner.username,
`@${users.user2.username} has joined Testcommunity! 🎉`
)
})

it('Leave community', async () => {
console.log('TEST 2')
const settingsModal = await new Sidebar(users.user1.app.driver).openSettings()
const isSettingsModal = await settingsModal.element.isDisplayed()
expect(isSettingsModal).toBeTruthy()
await settingsModal.openLeaveCommunityModal()
await settingsModal.leaveCommunityButton()
})

it('Leave community - Guest re-join to community successfully', async () => {
console.log('TEST 4')
const debugModal = new DebugModeModal(users.user1.app.driver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function* channelDeletionResponseSaga(
let newGeneralChannel: PublicChannelStorage | undefined = yield* select(publicChannelsSelectors.generalChannel)
while (!newGeneralChannel) {
log('General channel has not been replicated yet')
yield* delay(500)
yield* delay(1000)
newGeneralChannel = yield* select(publicChannelsSelectors.generalChannel)
}
yield* put(publicChannelsActions.setCurrentChannel({ channelId: newGeneralChannel.id }))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const log = logger('channels')
export function* channelsReplicatedSaga(
action: PayloadAction<ReturnType<typeof publicChannelsActions.channelsReplicated>['payload']>
): Generator {
log('Syncing channels')
// TODO: Refactor to use QuietLogger
log(`Syncing channels: ${JSON.stringify(action.payload, null, 2)}`)
const { channels } = action.payload
const _locallyStoredChannels = yield* select(publicChannelsSelectors.publicChannels)
const locallyStoredChannels = _locallyStoredChannels.map(channel => channel.id)
Expand All @@ -24,20 +25,10 @@ export function* channelsReplicatedSaga(
const databaseStoredChannelsIds = databaseStoredChannels.map(channel => channel.id)
console.log({ locallyStoredChannels, databaseStoredChannelsIds })

// Removing channels from store
if (databaseStoredChannelsIds.length > 0) {
for (const channelId of locallyStoredChannels) {
if (!databaseStoredChannelsIds.includes(channelId)) {
log(`Removing #${channelId} from store`)
yield* put(publicChannelsActions.deleteChannel({ channelId }))
yield* take(publicChannelsActions.completeChannelDeletion)
}
}
}

// Upserting channels to local storage
for (const channel of databaseStoredChannels) {
if (!locallyStoredChannels.includes(channel.id)) {
// TODO: Refactor to use QuietLogger
log(`Adding #${channel.name} to store`)
yield* put(
publicChannelsActions.addChannel({
Expand All @@ -52,6 +43,18 @@ export function* channelsReplicatedSaga(
}
}

// Removing channels from store
if (databaseStoredChannelsIds.length > 0) {
for (const channelId of locallyStoredChannels) {
if (!databaseStoredChannelsIds.includes(channelId)) {
// TODO: Refactor to use QuietLogger
log(`Removing #${channelId} from store`)
yield* put(publicChannelsActions.deleteChannel({ channelId }))
yield* take(publicChannelsActions.completeChannelDeletion)
}
}
}

const currentChannelCache = yield* select(publicChannelsSelectors.currentChannelMessages)
const currentChannelRepository = yield* select(messagesSelectors.currentPublicChannelMessagesEntries)

Expand Down

0 comments on commit 3a9529a

Please sign in to comment.