Convert polls to Typescript / Immutable Records (#29789)
This commit is contained in:
		
					parent
					
						
							
								e4e35ab134
							
						
					
				
			
			
				commit
				
					
						ded799f913
					
				
			
		
					 15 changed files with 272 additions and 186 deletions
				
			
		|  | @ -8,12 +8,11 @@ import type { | |||
|   ApiAccountRoleJSON, | ||||
|   ApiAccountJSON, | ||||
| } from 'mastodon/api_types/accounts'; | ||||
| import type { ApiCustomEmojiJSON } from 'mastodon/api_types/custom_emoji'; | ||||
| import emojify from 'mastodon/features/emoji/emoji'; | ||||
| import { unescapeHTML } from 'mastodon/utils/html'; | ||||
| 
 | ||||
| import { CustomEmojiFactory } from './custom_emoji'; | ||||
| import type { CustomEmoji } from './custom_emoji'; | ||||
| import { CustomEmojiFactory, makeEmojiMap } from './custom_emoji'; | ||||
| import type { CustomEmoji, EmojiMap } from './custom_emoji'; | ||||
| 
 | ||||
| // AccountField
 | ||||
| interface AccountFieldShape extends Required<ApiAccountFieldJSON> { | ||||
|  | @ -102,15 +101,6 @@ export const accountDefaultValues: AccountShape = { | |||
| 
 | ||||
| const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues); | ||||
| 
 | ||||
| type EmojiMap = Record<string, ApiCustomEmojiJSON>; | ||||
| 
 | ||||
| function makeEmojiMap(emojis: ApiCustomEmojiJSON[]) { | ||||
|   return emojis.reduce<EmojiMap>((obj, emoji) => { | ||||
|     obj[`:${emoji.shortcode}:`] = emoji; | ||||
|     return obj; | ||||
|   }, {}); | ||||
| } | ||||
| 
 | ||||
| function createAccountField( | ||||
|   jsonField: ApiAccountFieldJSON, | ||||
|   emojiMap: EmojiMap, | ||||
|  |  | |||
|  | @ -1,15 +1,32 @@ | |||
| import type { RecordOf } from 'immutable'; | ||||
| import { Record } from 'immutable'; | ||||
| import type { RecordOf, List as ImmutableList } from 'immutable'; | ||||
| import { Record as ImmutableRecord, isList } from 'immutable'; | ||||
| 
 | ||||
| import type { ApiCustomEmojiJSON } from 'mastodon/api_types/custom_emoji'; | ||||
| 
 | ||||
| type CustomEmojiShape = Required<ApiCustomEmojiJSON>; // no changes from server shape
 | ||||
| export type CustomEmoji = RecordOf<CustomEmojiShape>; | ||||
| 
 | ||||
| export const CustomEmojiFactory = Record<CustomEmojiShape>({ | ||||
| export const CustomEmojiFactory = ImmutableRecord<CustomEmojiShape>({ | ||||
|   shortcode: '', | ||||
|   static_url: '', | ||||
|   url: '', | ||||
|   category: '', | ||||
|   visible_in_picker: false, | ||||
| }); | ||||
| 
 | ||||
| export type EmojiMap = Record<string, ApiCustomEmojiJSON>; | ||||
| 
 | ||||
| export function makeEmojiMap( | ||||
|   emojis: ApiCustomEmojiJSON[] | ImmutableList<CustomEmoji>, | ||||
| ) { | ||||
|   if (isList(emojis)) { | ||||
|     return emojis.reduce<EmojiMap>((obj, emoji) => { | ||||
|       obj[`:${emoji.shortcode}:`] = emoji.toJS(); | ||||
|       return obj; | ||||
|     }, {}); | ||||
|   } else | ||||
|     return emojis.reduce<EmojiMap>((obj, emoji) => { | ||||
|       obj[`:${emoji.shortcode}:`] = emoji; | ||||
|       return obj; | ||||
|     }, {}); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										109
									
								
								app/javascript/mastodon/models/poll.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								app/javascript/mastodon/models/poll.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| import type { RecordOf } from 'immutable'; | ||||
| import { Record, List } from 'immutable'; | ||||
| 
 | ||||
| import escapeTextContentForBrowser from 'escape-html'; | ||||
| 
 | ||||
| import type { ApiPollJSON, ApiPollOptionJSON } from 'mastodon/api_types/polls'; | ||||
| import emojify from 'mastodon/features/emoji/emoji'; | ||||
| 
 | ||||
| import { CustomEmojiFactory, makeEmojiMap } from './custom_emoji'; | ||||
| import type { CustomEmoji, EmojiMap } from './custom_emoji'; | ||||
| 
 | ||||
| interface PollOptionTranslationShape { | ||||
|   title: string; | ||||
|   titleHtml: string; | ||||
| } | ||||
| 
 | ||||
| export type PollOptionTranslation = RecordOf<PollOptionTranslationShape>; | ||||
| 
 | ||||
| export const PollOptionTranslationFactory = Record<PollOptionTranslationShape>({ | ||||
|   title: '', | ||||
|   titleHtml: '', | ||||
| }); | ||||
| 
 | ||||
| interface PollOptionShape extends Required<ApiPollOptionJSON> { | ||||
|   voted: boolean; | ||||
|   titleHtml: string; | ||||
|   translation: PollOptionTranslation | null; | ||||
| } | ||||
| 
 | ||||
| export function createPollOptionTranslationFromServerJSON( | ||||
|   translation: { title: string }, | ||||
|   emojiMap: EmojiMap, | ||||
| ) { | ||||
|   return PollOptionTranslationFactory({ | ||||
|     ...translation, | ||||
|     titleHtml: emojify( | ||||
|       escapeTextContentForBrowser(translation.title), | ||||
|       emojiMap, | ||||
|     ), | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| export type PollOption = RecordOf<PollOptionShape>; | ||||
| 
 | ||||
| export const PollOptionFactory = Record<PollOptionShape>({ | ||||
|   title: '', | ||||
|   votes_count: 0, | ||||
|   voted: false, | ||||
|   titleHtml: '', | ||||
|   translation: null, | ||||
| }); | ||||
| 
 | ||||
| interface PollShape | ||||
|   extends Omit<ApiPollJSON, 'emojis' | 'options' | 'own_votes'> { | ||||
|   emojis: List<CustomEmoji>; | ||||
|   options: List<PollOption>; | ||||
|   own_votes?: List<number>; | ||||
| } | ||||
| export type Poll = RecordOf<PollShape>; | ||||
| 
 | ||||
| export const PollFactory = Record<PollShape>({ | ||||
|   id: '', | ||||
|   expires_at: '', | ||||
|   expired: false, | ||||
|   multiple: false, | ||||
|   voters_count: 0, | ||||
|   votes_count: 0, | ||||
|   voted: false, | ||||
|   emojis: List<CustomEmoji>(), | ||||
|   options: List<PollOption>(), | ||||
|   own_votes: List(), | ||||
| }); | ||||
| 
 | ||||
| export function createPollFromServerJSON( | ||||
|   serverJSON: ApiPollJSON, | ||||
|   previousPoll?: Poll, | ||||
| ) { | ||||
|   const emojiMap = makeEmojiMap(serverJSON.emojis); | ||||
| 
 | ||||
|   return PollFactory({ | ||||
|     ...serverJSON, | ||||
|     emojis: List(serverJSON.emojis.map((emoji) => CustomEmojiFactory(emoji))), | ||||
|     own_votes: serverJSON.own_votes ? List(serverJSON.own_votes) : undefined, | ||||
|     options: List( | ||||
|       serverJSON.options.map((optionJSON, index) => { | ||||
|         const option = PollOptionFactory({ | ||||
|           ...optionJSON, | ||||
|           voted: serverJSON.own_votes?.includes(index) || false, | ||||
|           titleHtml: emojify( | ||||
|             escapeTextContentForBrowser(optionJSON.title), | ||||
|             emojiMap, | ||||
|           ), | ||||
|         }); | ||||
| 
 | ||||
|         const prevOption = previousPoll?.options.get(index); | ||||
|         if (prevOption?.translation && prevOption.title === option.title) { | ||||
|           const { translation } = prevOption; | ||||
| 
 | ||||
|           option.set( | ||||
|             'translation', | ||||
|             createPollOptionTranslationFromServerJSON(translation, emojiMap), | ||||
|           ); | ||||
|         } | ||||
| 
 | ||||
|         return option; | ||||
|       }), | ||||
|     ), | ||||
|   }); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue