stream-chat-react-native-core 9.11.3 → 9.12.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +8 -5
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
- package/lib/commonjs/components/Poll/Poll.js +5 -2
- package/lib/commonjs/components/Poll/Poll.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js +3 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollOption.js +8 -3
- package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js +5 -2
- package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js +3 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollResults.js +4 -1
- package/lib/commonjs/components/Poll/components/PollResults/PollResults.js.map +1 -1
- package/lib/commonjs/components/Poll/hooks/usePollState.js +3 -0
- package/lib/commonjs/components/Poll/hooks/usePollState.js.map +1 -1
- package/lib/commonjs/components/Poll/hooks/usePollTextTranslation.js +17 -0
- package/lib/commonjs/components/Poll/hooks/usePollTextTranslation.js.map +1 -0
- package/lib/commonjs/components/Poll/index.js +11 -0
- package/lib/commonjs/components/Poll/index.js.map +1 -1
- package/lib/commonjs/hooks/messagePreview/useMessagePreviewText.js +9 -4
- package/lib/commonjs/hooks/messagePreview/useMessagePreviewText.js.map +1 -1
- package/lib/commonjs/hooks/useTranslatedMessage.js +4 -3
- package/lib/commonjs/hooks/useTranslatedMessage.js.map +1 -1
- package/lib/commonjs/store/SqliteClient.js +1 -1
- package/lib/commonjs/store/mappers/mapPollToStorable.js +4 -0
- package/lib/commonjs/store/mappers/mapPollToStorable.js.map +1 -1
- package/lib/commonjs/store/mappers/mapStorableToPoll.js +5 -0
- package/lib/commonjs/store/mappers/mapStorableToPoll.js.map +1 -1
- package/lib/commonjs/store/schema.js +2 -0
- package/lib/commonjs/store/schema.js.map +1 -1
- package/lib/commonjs/utils/i18n/getTranslatedText.js +7 -0
- package/lib/commonjs/utils/i18n/getTranslatedText.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +8 -5
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
- package/lib/module/components/Poll/Poll.js +5 -2
- package/lib/module/components/Poll/Poll.js.map +1 -1
- package/lib/module/components/Poll/components/PollAnswersList.js +3 -1
- package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/module/components/Poll/components/PollOption.js +8 -3
- package/lib/module/components/Poll/components/PollOption.js.map +1 -1
- package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js +5 -2
- package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
- package/lib/module/components/Poll/components/PollResults/PollResultItem.js +3 -1
- package/lib/module/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
- package/lib/module/components/Poll/components/PollResults/PollResults.js +4 -1
- package/lib/module/components/Poll/components/PollResults/PollResults.js.map +1 -1
- package/lib/module/components/Poll/hooks/usePollState.js +3 -0
- package/lib/module/components/Poll/hooks/usePollState.js.map +1 -1
- package/lib/module/components/Poll/hooks/usePollTextTranslation.js +17 -0
- package/lib/module/components/Poll/hooks/usePollTextTranslation.js.map +1 -0
- package/lib/module/components/Poll/index.js +11 -0
- package/lib/module/components/Poll/index.js.map +1 -1
- package/lib/module/hooks/messagePreview/useMessagePreviewText.js +9 -4
- package/lib/module/hooks/messagePreview/useMessagePreviewText.js.map +1 -1
- package/lib/module/hooks/useTranslatedMessage.js +4 -3
- package/lib/module/hooks/useTranslatedMessage.js.map +1 -1
- package/lib/module/store/SqliteClient.js +1 -1
- package/lib/module/store/mappers/mapPollToStorable.js +4 -0
- package/lib/module/store/mappers/mapPollToStorable.js.map +1 -1
- package/lib/module/store/mappers/mapStorableToPoll.js +5 -0
- package/lib/module/store/mappers/mapStorableToPoll.js.map +1 -1
- package/lib/module/store/schema.js +2 -0
- package/lib/module/store/schema.js.map +1 -1
- package/lib/module/utils/i18n/getTranslatedText.js +7 -0
- package/lib/module/utils/i18n/getTranslatedText.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewPollLabel.d.ts.map +1 -1
- package/lib/typescript/components/Poll/Poll.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollAnswersList.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollResults/PollOptionFullResults.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollResults/PollResultItem.d.ts.map +1 -1
- package/lib/typescript/components/Poll/components/PollResults/PollResults.d.ts.map +1 -1
- package/lib/typescript/components/Poll/hooks/usePollState.d.ts +2 -1
- package/lib/typescript/components/Poll/hooks/usePollState.d.ts.map +1 -1
- package/lib/typescript/components/Poll/hooks/usePollTextTranslation.d.ts +8 -0
- package/lib/typescript/components/Poll/hooks/usePollTextTranslation.d.ts.map +1 -0
- package/lib/typescript/components/Poll/index.d.ts +1 -0
- package/lib/typescript/components/Poll/index.d.ts.map +1 -1
- package/lib/typescript/hooks/messagePreview/useMessagePreviewText.d.ts.map +1 -1
- package/lib/typescript/hooks/useTranslatedMessage.d.ts.map +1 -1
- package/lib/typescript/store/mappers/mapPollToStorable.d.ts.map +1 -1
- package/lib/typescript/store/mappers/mapStorableToPoll.d.ts.map +1 -1
- package/lib/typescript/store/schema.d.ts +2 -0
- package/lib/typescript/store/schema.d.ts.map +1 -1
- package/lib/typescript/utils/i18n/getTranslatedText.d.ts +6 -0
- package/lib/typescript/utils/i18n/getTranslatedText.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewPollLabel.test.tsx +31 -6
- package/src/components/ChannelPreview/hooks/useChannelPreviewPollLabel.ts +8 -4
- package/src/components/Poll/Poll.tsx +4 -2
- package/src/components/Poll/__tests__/PollTranslation.test.tsx +152 -0
- package/src/components/Poll/components/PollAnswersList.tsx +5 -1
- package/src/components/Poll/components/PollOption.tsx +8 -4
- package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx +5 -2
- package/src/components/Poll/components/PollResults/PollResultItem.tsx +3 -1
- package/src/components/Poll/components/PollResults/PollResults.tsx +4 -2
- package/src/components/Poll/hooks/usePollState.ts +5 -0
- package/src/components/Poll/hooks/usePollTextTranslation.ts +21 -0
- package/src/components/Poll/index.ts +1 -0
- package/src/hooks/__tests__/useTranslatedMessage.test.tsx +20 -0
- package/src/hooks/messagePreview/__tests__/useMessagePreviewText.test.tsx +37 -2
- package/src/hooks/messagePreview/useMessagePreviewText.tsx +5 -2
- package/src/hooks/useTranslatedMessage.ts +7 -7
- package/src/store/SqliteClient.ts +1 -1
- package/src/store/apis/__tests__/pollTranslations.test.ts +136 -0
- package/src/store/mappers/mapPollToStorable.ts +6 -0
- package/src/store/mappers/mapStorableToPoll.ts +4 -0
- package/src/store/schema.ts +4 -0
- package/src/utils/i18n/__tests__/getTranslatedText.test.ts +19 -0
- package/src/utils/i18n/getTranslatedText.ts +9 -0
- package/src/version.json +1 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ChannelAPIResponse, PollResponse } from 'stream-chat';
|
|
2
|
+
|
|
3
|
+
import { generateChannelResponse } from '../../../mock-builders/generator/channel';
|
|
4
|
+
import { generateMessage } from '../../../mock-builders/generator/message';
|
|
5
|
+
import { BetterSqlite } from '../../../test-utils/BetterSqlite';
|
|
6
|
+
import { SqliteClient } from '../../SqliteClient';
|
|
7
|
+
import { getChannels } from '../getChannels';
|
|
8
|
+
import { upsertChannels } from '../upsertChannels';
|
|
9
|
+
import { upsertPoll } from '../upsertPoll';
|
|
10
|
+
|
|
11
|
+
const POLL_ID = 'poll-id';
|
|
12
|
+
|
|
13
|
+
const translatedPoll = {
|
|
14
|
+
answers_count: 0,
|
|
15
|
+
created_at: '2026-10-27T10:00:00.000Z',
|
|
16
|
+
created_by: { id: 'other-user' },
|
|
17
|
+
created_by_id: 'other-user',
|
|
18
|
+
description: 'For the team dinner',
|
|
19
|
+
description_i18n: { es_text: 'Para la cena del equipo', language: 'en' },
|
|
20
|
+
enforce_unique_vote: true,
|
|
21
|
+
id: POLL_ID,
|
|
22
|
+
latest_answers: [],
|
|
23
|
+
latest_votes_by_option: {},
|
|
24
|
+
max_votes_allowed: 1,
|
|
25
|
+
name: 'Favourite colour?',
|
|
26
|
+
name_i18n: { es_text: '¿Color favorito?', language: 'en' },
|
|
27
|
+
options: [
|
|
28
|
+
{
|
|
29
|
+
id: 'option-red',
|
|
30
|
+
poll_id: POLL_ID,
|
|
31
|
+
text: 'Red',
|
|
32
|
+
text_i18n: { es_text: 'Rojo', language: 'en' },
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
updated_at: '2026-10-27T10:00:00.000Z',
|
|
36
|
+
vote_count: 0,
|
|
37
|
+
vote_counts_by_option: {},
|
|
38
|
+
} as unknown as PollResponse;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The backend translates poll texts. The offline DB has to keep those translations, so a cold start
|
|
42
|
+
* offline shows the poll in the user's language, the same way message translations are kept.
|
|
43
|
+
*/
|
|
44
|
+
describe('poll translations in the offline DB', () => {
|
|
45
|
+
let cid: string;
|
|
46
|
+
|
|
47
|
+
beforeEach(async () => {
|
|
48
|
+
await SqliteClient.initializeDatabase();
|
|
49
|
+
await BetterSqlite.openDB();
|
|
50
|
+
|
|
51
|
+
const channelResponse = generateChannelResponse({ members: [], messages: [] });
|
|
52
|
+
cid = channelResponse.channel.cid as string;
|
|
53
|
+
channelResponse.messages = [
|
|
54
|
+
generateMessage({ cid, poll: translatedPoll, poll_id: POLL_ID, text: '' }),
|
|
55
|
+
] as unknown as typeof channelResponse.messages;
|
|
56
|
+
await upsertChannels({
|
|
57
|
+
channels: [channelResponse] as unknown as ChannelAPIResponse[],
|
|
58
|
+
isLatestMessagesSet: true,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
BetterSqlite.dropAllTables();
|
|
64
|
+
BetterSqlite.closeDB();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const getStoredPoll = async () => {
|
|
68
|
+
const [channel] = await getChannels({ channelIds: [cid], currentUserId: 'some-user' });
|
|
69
|
+
return channel.messages[0].poll;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
it("keeps the question's, description's and options' translations", async () => {
|
|
73
|
+
const poll = await getStoredPoll();
|
|
74
|
+
|
|
75
|
+
expect(poll?.name_i18n).toEqual(translatedPoll.name_i18n);
|
|
76
|
+
expect(poll?.description_i18n).toEqual(translatedPoll.description_i18n);
|
|
77
|
+
expect(poll?.options[0].text_i18n).toEqual(translatedPoll.options[0].text_i18n);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('replaces the translations when the poll is stored again', async () => {
|
|
81
|
+
const name_i18n = { de_text: 'Lieblingsfarbe?', language: 'en' };
|
|
82
|
+
await upsertPoll({ poll: { ...translatedPoll, name_i18n } as PollResponse });
|
|
83
|
+
|
|
84
|
+
expect((await getStoredPoll())?.name_i18n).toEqual(name_i18n);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('stores a poll without translations unchanged', async () => {
|
|
88
|
+
const {
|
|
89
|
+
description_i18n: _description_i18n,
|
|
90
|
+
name_i18n: _name_i18n,
|
|
91
|
+
...untranslatedPoll
|
|
92
|
+
} = translatedPoll;
|
|
93
|
+
const untranslatedChannel = generateChannelResponse({ members: [], messages: [] });
|
|
94
|
+
const untranslatedCid = untranslatedChannel.channel.cid as string;
|
|
95
|
+
untranslatedChannel.messages = [
|
|
96
|
+
generateMessage({
|
|
97
|
+
cid: untranslatedCid,
|
|
98
|
+
poll: {
|
|
99
|
+
...untranslatedPoll,
|
|
100
|
+
id: 'untranslated-poll-id',
|
|
101
|
+
options: [{ id: 'option-red', poll_id: 'untranslated-poll-id', text: 'Red' }],
|
|
102
|
+
} as unknown as PollResponse,
|
|
103
|
+
poll_id: 'untranslated-poll-id',
|
|
104
|
+
text: '',
|
|
105
|
+
}),
|
|
106
|
+
] as unknown as typeof untranslatedChannel.messages;
|
|
107
|
+
await upsertChannels({
|
|
108
|
+
channels: [untranslatedChannel] as unknown as ChannelAPIResponse[],
|
|
109
|
+
isLatestMessagesSet: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const [channel] = await getChannels({
|
|
113
|
+
channelIds: [untranslatedCid],
|
|
114
|
+
currentUserId: 'some-user',
|
|
115
|
+
});
|
|
116
|
+
const poll = channel.messages[0].poll;
|
|
117
|
+
|
|
118
|
+
expect(poll?.name).toBe('Favourite colour?');
|
|
119
|
+
expect(poll?.description).toBe('For the team dinner');
|
|
120
|
+
expect(poll?.options).toEqual([
|
|
121
|
+
{ id: 'option-red', poll_id: 'untranslated-poll-id', text: 'Red' },
|
|
122
|
+
]);
|
|
123
|
+
expect(poll?.name_i18n).toBeUndefined();
|
|
124
|
+
expect(poll?.description_i18n).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('clears translations that the poll no longer has', async () => {
|
|
128
|
+
await upsertPoll({
|
|
129
|
+
poll: { ...translatedPoll, description_i18n: undefined, name_i18n: undefined },
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const poll = await getStoredPoll();
|
|
133
|
+
expect(poll?.name_i18n).toBeUndefined();
|
|
134
|
+
expect(poll?.description_i18n).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -13,6 +13,7 @@ export const mapPollToStorable = (poll: PollResponse): TableRow<'poll'> => {
|
|
|
13
13
|
created_by,
|
|
14
14
|
created_by_id,
|
|
15
15
|
description,
|
|
16
|
+
description_i18n,
|
|
16
17
|
enforce_unique_vote,
|
|
17
18
|
id,
|
|
18
19
|
is_closed,
|
|
@@ -20,6 +21,7 @@ export const mapPollToStorable = (poll: PollResponse): TableRow<'poll'> => {
|
|
|
20
21
|
latest_votes_by_option,
|
|
21
22
|
max_votes_allowed,
|
|
22
23
|
name,
|
|
24
|
+
name_i18n,
|
|
23
25
|
options,
|
|
24
26
|
own_votes,
|
|
25
27
|
updated_at,
|
|
@@ -36,6 +38,9 @@ export const mapPollToStorable = (poll: PollResponse): TableRow<'poll'> => {
|
|
|
36
38
|
created_by: JSON.stringify(created_by), // decouple the users from the actual poll
|
|
37
39
|
created_by_id,
|
|
38
40
|
description,
|
|
41
|
+
// Always written (as `null` when absent): an upsert skips undefined columns, which would keep
|
|
42
|
+
// translations the server has since dropped, e.g. after the question was edited.
|
|
43
|
+
description_i18n: JSON.stringify(description_i18n ?? null),
|
|
39
44
|
enforce_unique_vote,
|
|
40
45
|
id,
|
|
41
46
|
is_closed,
|
|
@@ -43,6 +48,7 @@ export const mapPollToStorable = (poll: PollResponse): TableRow<'poll'> => {
|
|
|
43
48
|
latest_votes_by_option: JSON.stringify(latest_votes_by_option),
|
|
44
49
|
max_votes_allowed,
|
|
45
50
|
name,
|
|
51
|
+
name_i18n: JSON.stringify(name_i18n ?? null),
|
|
46
52
|
options: JSON.stringify(options),
|
|
47
53
|
own_votes: JSON.stringify(own_votes),
|
|
48
54
|
updated_at,
|
|
@@ -11,6 +11,7 @@ export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponse => {
|
|
|
11
11
|
created_by,
|
|
12
12
|
created_by_id,
|
|
13
13
|
description,
|
|
14
|
+
description_i18n,
|
|
14
15
|
enforce_unique_vote,
|
|
15
16
|
id,
|
|
16
17
|
is_closed,
|
|
@@ -18,6 +19,7 @@ export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponse => {
|
|
|
18
19
|
latest_votes_by_option,
|
|
19
20
|
max_votes_allowed,
|
|
20
21
|
name,
|
|
22
|
+
name_i18n,
|
|
21
23
|
options,
|
|
22
24
|
own_votes,
|
|
23
25
|
updated_at,
|
|
@@ -34,6 +36,7 @@ export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponse => {
|
|
|
34
36
|
created_by: JSON.parse(created_by),
|
|
35
37
|
created_by_id,
|
|
36
38
|
description,
|
|
39
|
+
description_i18n: description_i18n ? (JSON.parse(description_i18n) ?? undefined) : undefined,
|
|
37
40
|
enforce_unique_vote,
|
|
38
41
|
id,
|
|
39
42
|
is_closed,
|
|
@@ -41,6 +44,7 @@ export const mapStorableToPoll = (pollRow: TableRow<'poll'>): PollResponse => {
|
|
|
41
44
|
latest_votes_by_option: JSON.parse(latest_votes_by_option),
|
|
42
45
|
max_votes_allowed,
|
|
43
46
|
name,
|
|
47
|
+
name_i18n: name_i18n ? (JSON.parse(name_i18n) ?? undefined) : undefined,
|
|
44
48
|
options: JSON.parse(options),
|
|
45
49
|
own_votes: own_votes ? JSON.parse(own_votes) : [],
|
|
46
50
|
updated_at,
|
package/src/store/schema.ts
CHANGED
|
@@ -224,6 +224,7 @@ export const tables: Tables = {
|
|
|
224
224
|
created_by: 'TEXT',
|
|
225
225
|
created_by_id: 'TEXT',
|
|
226
226
|
description: 'TEXT',
|
|
227
|
+
description_i18n: 'TEXT',
|
|
227
228
|
enforce_unique_vote: 'BOOLEAN DEFAULT FALSE',
|
|
228
229
|
id: 'TEXT NOT NULL',
|
|
229
230
|
is_closed: 'BOOLEAN DEFAULT FALSE',
|
|
@@ -231,6 +232,7 @@ export const tables: Tables = {
|
|
|
231
232
|
latest_votes_by_option: 'TEXT',
|
|
232
233
|
max_votes_allowed: 'INTEGER DEFAULT 1',
|
|
233
234
|
name: 'TEXT',
|
|
235
|
+
name_i18n: 'TEXT',
|
|
234
236
|
options: 'TEXT',
|
|
235
237
|
own_votes: 'TEXT',
|
|
236
238
|
updated_at: 'TEXT',
|
|
@@ -459,7 +461,9 @@ export type Schema = {
|
|
|
459
461
|
allow_answers?: boolean;
|
|
460
462
|
allow_user_suggested_options?: boolean;
|
|
461
463
|
description?: string;
|
|
464
|
+
description_i18n?: string;
|
|
462
465
|
is_closed?: boolean;
|
|
466
|
+
name_i18n?: string;
|
|
463
467
|
own_votes?: string;
|
|
464
468
|
voting_visibility?: string;
|
|
465
469
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { TranslatedField } from 'stream-chat';
|
|
2
|
+
|
|
3
|
+
import { getTranslatedText } from '../getTranslatedText';
|
|
4
|
+
|
|
5
|
+
describe('getTranslatedText', () => {
|
|
6
|
+
const i18n = { de_text: 'Wie geht es?', language: 'en' } as TranslatedField;
|
|
7
|
+
|
|
8
|
+
it('returns the translation into the given language', () => {
|
|
9
|
+
expect(getTranslatedText(i18n, 'de')).toBe('Wie geht es?');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('returns undefined when there is no translation into the given language', () => {
|
|
13
|
+
expect(getTranslatedText(i18n, 'fr')).toBeUndefined();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns undefined when the text has no translations', () => {
|
|
17
|
+
expect(getTranslatedText(undefined, 'de')).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TranslatedField, TranslationLanguages } from 'stream-chat';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the translation of a text into `language`, or undefined if there is none.
|
|
5
|
+
*/
|
|
6
|
+
export const getTranslatedText = (
|
|
7
|
+
i18n: TranslatedField | undefined,
|
|
8
|
+
language: TranslationLanguages,
|
|
9
|
+
): string | undefined => i18n?.[`${language}_text` as `${TranslationLanguages}_text`];
|
package/src/version.json
CHANGED