When streaming API is disconnected, poll home/notifications (#2776)
* When streaming API is disconnected, poll home/notifications Display slightly different empty home timeline message if user is following others Cull notifications to 20 items when over 40 get added in real-time Run manage:translations * Optimize <HomeTimeline /> a little
This commit is contained in:
		
					parent
					
						
							
								84eb425f38
							
						
					
				
			
			
				commit
				
					
						eddb95b012
					
				
			
		
					 31 changed files with 124 additions and 45 deletions
				
			
		|  | @ -54,54 +54,64 @@ const excludeTypesFromSettings = state => state.getIn(['settings', 'notification | |||
| 
 | ||||
| export function refreshNotifications() { | ||||
|   return (dispatch, getState) => { | ||||
|     dispatch(refreshNotificationsRequest()); | ||||
| 
 | ||||
|     const params = {}; | ||||
|     const ids    = getState().getIn(['notifications', 'items']); | ||||
| 
 | ||||
|     let skipLoading = false; | ||||
| 
 | ||||
|     if (ids.size > 0) { | ||||
|       params.since_id = ids.first().get('id'); | ||||
|     } | ||||
| 
 | ||||
|     if (getState().getIn(['notifications', 'loaded'])) { | ||||
|       skipLoading = true; | ||||
|     } | ||||
| 
 | ||||
|     params.exclude_types = excludeTypesFromSettings(getState()); | ||||
| 
 | ||||
|     dispatch(refreshNotificationsRequest(skipLoading)); | ||||
| 
 | ||||
|     api(getState).get('/api/v1/notifications', { params }).then(response => { | ||||
|       const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||
| 
 | ||||
|       dispatch(refreshNotificationsSuccess(response.data, next ? next.uri : null)); | ||||
|       dispatch(refreshNotificationsSuccess(response.data, skipLoading, next ? next.uri : null)); | ||||
|       fetchRelatedRelationships(dispatch, response.data); | ||||
|     }).catch(error => { | ||||
|       dispatch(refreshNotificationsFail(error)); | ||||
|       dispatch(refreshNotificationsFail(error, skipLoading)); | ||||
|     }); | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function refreshNotificationsRequest() { | ||||
| export function refreshNotificationsRequest(skipLoading) { | ||||
|   return { | ||||
|     type: NOTIFICATIONS_REFRESH_REQUEST | ||||
|     type: NOTIFICATIONS_REFRESH_REQUEST, | ||||
|     skipLoading | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function refreshNotificationsSuccess(notifications, next) { | ||||
| export function refreshNotificationsSuccess(notifications, skipLoading, next) { | ||||
|   return { | ||||
|     type: NOTIFICATIONS_REFRESH_SUCCESS, | ||||
|     notifications, | ||||
|     accounts: notifications.map(item => item.account), | ||||
|     statuses: notifications.map(item => item.status).filter(status => !!status), | ||||
|     skipLoading, | ||||
|     next | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function refreshNotificationsFail(error) { | ||||
| export function refreshNotificationsFail(error, skipLoading) { | ||||
|   return { | ||||
|     type: NOTIFICATIONS_REFRESH_FAIL, | ||||
|     error | ||||
|     error, | ||||
|     skipLoading | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function expandNotifications() { | ||||
|   return (dispatch, getState) => { | ||||
|     const url    = getState().getIn(['notifications', 'next'], null); | ||||
|     const lastId = getState().getIn(['notifications', 'items']).last(); | ||||
| 
 | ||||
|     if (url === null || getState().getIn(['notifications', 'isLoading'])) { | ||||
|       return; | ||||
|  | @ -109,7 +119,10 @@ export function expandNotifications() { | |||
| 
 | ||||
|     dispatch(expandNotificationsRequest()); | ||||
| 
 | ||||
|     const params = {}; | ||||
|     const params = { | ||||
|       max_id: lastId, | ||||
|       limit: 20 | ||||
|     }; | ||||
| 
 | ||||
|     params.exclude_types = excludeTypesFromSettings(getState()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -86,6 +86,8 @@ export function refreshTimeline(timeline, id = null) { | |||
| 
 | ||||
|       params          = { ...params, since_id: newestId }; | ||||
|       skipLoading     = true; | ||||
|     } else if (getState().getIn(['timelines', timeline, 'loaded'])) { | ||||
|       skipLoading = true; | ||||
|     } | ||||
| 
 | ||||
|     dispatch(refreshTimelineRequest(timeline, id, skipLoading)); | ||||
|  |  | |||
|  | @ -217,20 +217,34 @@ Container.propTypes = { | |||
|   children: PropTypes.node, | ||||
| }; | ||||
| 
 | ||||
| class Mastodon extends React.Component { | ||||
| class Mastodon extends React.PureComponent { | ||||
| 
 | ||||
|   componentDidMount() { | ||||
|     const { locale }  = this.props; | ||||
|     const streamingAPIBaseURL = store.getState().getIn(['meta', 'streaming_api_base_url']); | ||||
|     const accessToken = store.getState().getIn(['meta', 'access_token']); | ||||
| 
 | ||||
|     const setupPolling = () => { | ||||
|       this.polling = setInterval(() => { | ||||
|         store.dispatch(refreshTimeline('home')); | ||||
|         store.dispatch(refreshNotifications()); | ||||
|       }, 20000); | ||||
|     }; | ||||
| 
 | ||||
|     const clearPolling = () => { | ||||
|       clearInterval(this.polling); | ||||
|       this.polling = undefined; | ||||
|     }; | ||||
| 
 | ||||
|     this.subscription = createStream(streamingAPIBaseURL, accessToken, 'user', { | ||||
| 
 | ||||
|       connected () { | ||||
|         clearPolling(); | ||||
|         store.dispatch(connectTimeline('home')); | ||||
|       }, | ||||
| 
 | ||||
|       disconnected () { | ||||
|         setupPolling(); | ||||
|         store.dispatch(disconnectTimeline('home')); | ||||
|       }, | ||||
| 
 | ||||
|  | @ -249,6 +263,7 @@ class Mastodon extends React.Component { | |||
|       }, | ||||
| 
 | ||||
|       reconnected () { | ||||
|         clearPolling(); | ||||
|         store.dispatch(connectTimeline('home')); | ||||
|         store.dispatch(refreshTimeline('home')); | ||||
|         store.dispatch(refreshNotifications()); | ||||
|  | @ -269,6 +284,11 @@ class Mastodon extends React.Component { | |||
|       this.subscription.close(); | ||||
|       this.subscription = null; | ||||
|     } | ||||
| 
 | ||||
|     if (typeof this.polling !== 'undefined') { | ||||
|       clearInterval(this.polling); | ||||
|       this.polling = null; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|  |  | |||
|  | @ -12,18 +12,33 @@ const messages = defineMessages({ | |||
| }); | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0 | ||||
|   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, | ||||
|   hasFollows: state.getIn(['accounts_counters', state.getIn(['meta', 'me']), 'following_count']) > 0 | ||||
| }); | ||||
| 
 | ||||
| class HomeTimeline extends React.PureComponent { | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl, hasUnread } = this.props; | ||||
|     const { intl, hasUnread, hasFollows } = this.props; | ||||
| 
 | ||||
|     let emptyMessage; | ||||
| 
 | ||||
|     if (hasFollows) { | ||||
|       emptyMessage = <FormattedMessage id='empty_column.home.inactivity' defaultMessage="Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon." /> | ||||
|     } else { | ||||
|       emptyMessage = <FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <Column icon='home' active={hasUnread} heading={intl.formatMessage(messages.title)}> | ||||
|         <ColumnSettingsContainer /> | ||||
|         <StatusListContainer {...this.props} scrollKey='home_timeline' type='home' emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} /> | ||||
| 
 | ||||
|         <StatusListContainer | ||||
|           {...this.props} | ||||
|           scrollKey='home_timeline' | ||||
|           type='home' | ||||
|           emptyMessage={emptyMessage} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   } | ||||
|  | @ -32,7 +47,8 @@ class HomeTimeline extends React.PureComponent { | |||
| 
 | ||||
| HomeTimeline.propTypes = { | ||||
|   intl: PropTypes.object.isRequired, | ||||
|   hasUnread: PropTypes.bool | ||||
|   hasUnread: PropTypes.bool, | ||||
|   hasFollows: PropTypes.bool | ||||
| }; | ||||
| 
 | ||||
| export default connect(mapStateToProps)(injectIntl(HomeTimeline)); | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
|   "compose_form.lock_disclaimer": "حسابك ليس {locked}. يمكن لأي شخص متابعتك و عرض المنشورات.", | ||||
|   "compose_form.lock_disclaimer.lock": "مقفل", | ||||
|   "compose_form.placeholder": "فيمَ تفكّر؟", | ||||
|   "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.", | ||||
|   "compose_form.publish": "بوّق !", | ||||
|   "compose_form.sensitive": "ضع علامة على الوسيط باعتباره حسّاس", | ||||
|   "compose_form.spoiler": "أخفِ النص واعرض تحذيرا", | ||||
|  | @ -53,6 +54,7 @@ | |||
|   "empty_column.community": "الخط الزمني المحلي فارغ. اكتب شيئا ما للعامة كبداية.", | ||||
|   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", | ||||
|   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "الخيط العام", | ||||
|   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", | ||||
|   "empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.", | ||||
|  | @ -149,24 +151,14 @@ | |||
|   "tabs_bar.compose": "تحرير", | ||||
|   "tabs_bar.federated_timeline": "Federated", | ||||
|   "tabs_bar.home": "الرئيسية", | ||||
|   "tabs_bar.mentions": "الإشارات", | ||||
|   "tabs_bar.public": "الخيط العام الموحد", | ||||
|   "tabs_bar.local_timeline": "Local", | ||||
|   "tabs_bar.notifications": "الإخطارات", | ||||
|   "upload_area.title": "Drag & drop to upload", | ||||
|   "upload_button.label": "إضافة وسائط", | ||||
|   "upload_form.undo": "إلغاء", | ||||
|   "upload_progress.label": "يرفع...", | ||||
|   "notification.follow": "{name} يتبعك", | ||||
|   "notification.favourite": "{name} أعجب بمنشورك", | ||||
|   "notification.reblog": "{name} قام بترقية تبويقك", | ||||
|   "notification.mention": "{name} ذكرك", | ||||
|   "notifications.column_settings.alert": "إشعارات سطح المكتب", | ||||
|   "notifications.column_settings.show": "إعرِضها في عمود", | ||||
|   "notifications.column_settings.follow": "متابعُون جُدُد :", | ||||
|   "notifications.column_settings.favourite": "المُفَضَّلة :", | ||||
|   "notifications.column_settings.mention": "الإشارات :", | ||||
|   "notifications.column_settings.reblog": "الترقيّات:", | ||||
|   "video_player.expand": "وسّع الفيديو", | ||||
|   "video_player.toggle_sound": "تبديل الصوت", | ||||
|   "video_player.toggle_visible": "إظهار / إخفاء الفيديو", | ||||
|   "video_player.expand": "وسّع الفيديو", | ||||
|   "video_player.video_error": "تعذر تشغيل الفيديو" | ||||
| } | ||||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe etwas öffentlich, um den Ball ins Rollen zu bringen!", | ||||
|   "empty_column.hashtag": "Es gibt noch nichts unter diesem Hashtag.", | ||||
|   "empty_column.home": "Du folgst noch niemandem. Besuche {public} oder benutze die Suche, um zu starten oder andere Benutzer anzutreffen.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "die öffentliche Zeitleiste", | ||||
|   "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um die Konversation zu starten.", | ||||
|   "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Benutzern von anderen Instanzen, um es aufzufüllen.", | ||||
|  |  | |||
|  | @ -713,6 +713,10 @@ | |||
|       { | ||||
|         "defaultMessage": "the public timeline", | ||||
|         "id": "empty_column.home.public_timeline" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|         "id": "empty_column.home.inactivity" | ||||
|       } | ||||
|     ], | ||||
|     "path": "app/javascript/mastodon/features/home_timeline/index.json" | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "فهرست نوشتههای محلی خالی است. چیزی بنویسید تا چرخش بچرخد!", | ||||
|   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", | ||||
|   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "فهرست نوشتههای همهجا", | ||||
|   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشتههای دیگران واکنش نشان دهید تا گفتگو آغاز شود.", | ||||
|   "empty_column.public": "اینجا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا اینجا پر شود", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -30,7 +30,6 @@ | |||
|   "compose_form.lock_disclaimer.lock": "locked", | ||||
|   "compose_form.placeholder": "Qu’avez-vous en tête ?", | ||||
|   "compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.", | ||||
|   "compose_form.private": "Rendre privé", | ||||
|   "compose_form.publish": "Pouet ", | ||||
|   "compose_form.sensitive": "Marquer le média comme délicat", | ||||
|   "compose_form.spoiler": "Masquer le texte derrière un avertissement", | ||||
|  | @ -54,8 +53,9 @@ | |||
|   "emoji_button.travel": "Travel & Places", | ||||
|   "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !", | ||||
|   "empty_column.hashtag": "Il n’y a encore aucun contenu relatif à ce hashtag", | ||||
|   "empty_column.home.public_timeline": "le fil public", | ||||
|   "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateurs⋅trices.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "le fil public", | ||||
|   "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateurs⋅trices pour débuter la conversation.", | ||||
|   "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateurs⋅trices d’autres instances pour remplir le fil public.", | ||||
|   "follow_request.authorize": "Autoriser", | ||||
|  | @ -106,6 +106,7 @@ | |||
|   "onboarding.page_one.welcome": "Bienvenue sur Mastodon !", | ||||
|   "onboarding.page_six.admin": "L’administrateur⋅trice de votre instance est {admin}", | ||||
|   "onboarding.page_six.almost_done": "Nous y sommes presque…", | ||||
|   "onboarding.page_six.appetoot": "Bon Appetoot!", | ||||
|   "onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres. Et maintenant… Bon Appetoot!", | ||||
|   "onboarding.page_six.github": "Mastodon est un logiciel libre, gratuit et open-source. Vous pouvez rapporter des bogues, suggérer des fonctionnalités, ou contribuer à son développement sur {github}.", | ||||
|   "onboarding.page_six.guidelines": "règles de la communauté", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!", | ||||
|   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.", | ||||
|   "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "javni timeline", | ||||
|   "empty_column.notifications": "Još nemaš notifikacija. Komuniciraj sa drugima kako bi započeo razgovor.", | ||||
|   "empty_column.public": "Ovdje nema ništa! Napiši nešto javno, ili ručno slijedi korisnike sa drugih instanci kako bi popunio", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!", | ||||
|   "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.", | ||||
|   "empty_column.home": "Anda sedang tidak mengikuti siapapun. Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "linimasa publik", | ||||
|   "empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.", | ||||
|   "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisinya secara manual", | ||||
|  | @ -129,9 +130,6 @@ | |||
|   "report.placeholder": "Komentar tambahan", | ||||
|   "report.submit": "Kirim", | ||||
|   "report.target": "Melaporkan", | ||||
|   "search.status_by": "Status yang dibuat oleh {name}", | ||||
|   "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}", | ||||
|   "status.cannot_reblog": "Postingan ini tidak dapat di-boost", | ||||
|   "search.placeholder": "Pencarian", | ||||
|   "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}", | ||||
|   "status.cannot_reblog": "This post cannot be boosted", | ||||
|  | @ -162,6 +160,5 @@ | |||
|   "video_player.expand": "Tampilkan video", | ||||
|   "video_player.toggle_sound": "Suara", | ||||
|   "video_player.toggle_visible": "Tampilan", | ||||
|   "video_player.expand": "Tampilkan video", | ||||
|   "video_player.video_error": "Video tidak dapat diputar" | ||||
| } | ||||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!", | ||||
|   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.", | ||||
|   "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "la publika tempolineo", | ||||
|   "empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.", | ||||
|   "empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!", | ||||
|   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.", | ||||
|   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "la timeline pubblica", | ||||
|   "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.", | ||||
|   "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!", | ||||
|   "empty_column.hashtag": "このハッシュタグはまだ使われていません。", | ||||
|   "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "連合タイムライン", | ||||
|   "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。", | ||||
|   "empty_column.public": "ここにはまだ何もありません!公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう!", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -4,8 +4,8 @@ | |||
|   "account.edit_profile": "Rediger profil", | ||||
|   "account.follow": "Følg", | ||||
|   "account.followers": "Følgere", | ||||
|   "account.follows_you": "Følger deg", | ||||
|   "account.follows": "Følger", | ||||
|   "account.follows_you": "Følger deg", | ||||
|   "account.mention": "Nevn @{name}", | ||||
|   "account.mute": "Demp @{name}", | ||||
|   "account.posts": "Innlegg", | ||||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!", | ||||
|   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.", | ||||
|   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "en offentlig tidslinje", | ||||
|   "empty_column.notifications": "Du har ingen varsler ennå. Kommuniser med andre for å begynne samtalen.", | ||||
|   "empty_column.public": "Det er ingenting her! Skriv noe offentlig, eller følg brukere manuelt fra andre instanser for å fylle den opp", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Lo fil public local es void. Escribètz quicòm per lo garnir !", | ||||
|   "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag", | ||||
|   "empty_column.home": "Pel moment segètz pas segun. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "lo fil public", | ||||
|   "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.", | ||||
|   "empty_column.public": "I a pas res aquí ! Escribètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo fil public.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Ainda não existem conteúdo local para mostrar!", | ||||
|   "empty_column.hashtag": "Ainda não existe qualquer conteúdo com essa hashtag", | ||||
|   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "global", | ||||
|   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", | ||||
|   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Ainda não existem conteúdo local para mostrar!", | ||||
|   "empty_column.hashtag": "Ainda não existe qualquer conteúdo com essa hashtag", | ||||
|   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "global", | ||||
|   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", | ||||
|   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!", | ||||
|   "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.", | ||||
|   "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "публичные ленты", | ||||
|   "empty_column.notifications": "У Вас еще нет уведомлений. Заведите знакомство с другими пользователями, чтобы начать разговор.", | ||||
|   "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту.", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", | ||||
|   "empty_column.hashtag": "There is nothing in this hashtag yet.", | ||||
|   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "the public timeline", | ||||
|   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", | ||||
|   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ | |||
|   "column.mutes": "被静音的用户", | ||||
|   "column.notifications": "通知", | ||||
|   "column.public": "跨站公共时间轴", | ||||
|   "column_back_button.label": "Back", | ||||
|   "column_subheading.navigation": "导航", | ||||
|   "column_subheading.settings": "设置", | ||||
|   "compose_form.lock_disclaimer": "你的账户没 {locked}. 任何人可以通过关注你来查看只有关注者可见的嘟文.", | ||||
|  | @ -33,7 +34,6 @@ | |||
|   "compose_form.sensitive": "将媒体文件标示为“敏感内容”", | ||||
|   "compose_form.spoiler": "将部分文本藏于警告消息之后", | ||||
|   "compose_form.spoiler_placeholder": "敏感内容的警告消息", | ||||
|   "emoji_button.label": "加入表情符号", | ||||
|   "confirmation_modal.cancel": "取消", | ||||
|   "confirmations.block.confirm": "屏蔽", | ||||
|   "confirmations.block.message": "想好了,真的要屏蔽 {name}?", | ||||
|  | @ -44,6 +44,7 @@ | |||
|   "emoji_button.activity": "活动", | ||||
|   "emoji_button.flags": "旗帜", | ||||
|   "emoji_button.food": "食物和饮料", | ||||
|   "emoji_button.label": "加入表情符号", | ||||
|   "emoji_button.nature": "自然", | ||||
|   "emoji_button.objects": "物体", | ||||
|   "emoji_button.people": "人物", | ||||
|  | @ -53,6 +54,7 @@ | |||
|   "empty_column.community": "本站时间轴暂时未有内容,快贴文来抢头香啊!", | ||||
|   "empty_column.hashtag": "这个标签暂时未有内容。", | ||||
|   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "公共时间轴", | ||||
|   "empty_column.notifications": "你没有任何通知纪录,快向其他用户搭讪吧。", | ||||
|   "empty_column.public": "跨站公共时间轴暂时没有内容!快写一些公共的嘟文,或者关注另一些服务站的用户吧!你和本站、友站的交流,将决定这里出现的内容。", | ||||
|  | @ -129,8 +131,8 @@ | |||
|   "report.submit": "提交", | ||||
|   "report.target": "Reporting", | ||||
|   "search.placeholder": "搜索", | ||||
|   "search_results.total": "{count, number} {count, plural, one {result} other {results}}", | ||||
|   "status.cannot_reblog": "没法转嘟这条嘟文啦……", | ||||
|   "search.status_by": "按{name}搜索嘟文", | ||||
|   "status.delete": "删除", | ||||
|   "status.favourite": "赞", | ||||
|   "status.load_more": "加载更多", | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ | |||
|   "empty_column.community": "本站時間軸暫時未有內容,快貼文來搶頭香啊!", | ||||
|   "empty_column.hashtag": "這個標籤暫時未有內容。", | ||||
|   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", | ||||
|   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.", | ||||
|   "empty_column.home.public_timeline": "公共時間軸", | ||||
|   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。", | ||||
|   "empty_column.public": "跨站公共時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。", | ||||
|  |  | |||
|  | @ -87,7 +87,11 @@ const initialState = Immutable.Map(); | |||
| export default function accountsCounters(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|   case STORE_HYDRATE: | ||||
|     return state.merge(action.state.get('accounts_counters')); | ||||
|     return state.merge(action.state.get('accounts').map(item => Immutable.fromJS({ | ||||
|       followers_count: item.get('followers_count'), | ||||
|       following_count: item.get('following_count'), | ||||
|       statuses_count: item.get('statuses_count') | ||||
|     }))); | ||||
|   case ACCOUNT_FETCH_SUCCESS: | ||||
|   case NOTIFICATIONS_UPDATE: | ||||
|     return normalizeAccount(state, action.account); | ||||
|  |  | |||
|  | @ -29,11 +29,19 @@ const notificationToMap = notification => Immutable.Map({ | |||
| }); | ||||
| 
 | ||||
| const normalizeNotification = (state, notification) => { | ||||
|   if (!state.get('top')) { | ||||
|   const top = state.get('top'); | ||||
| 
 | ||||
|   if (!top) { | ||||
|     state = state.update('unread', unread => unread + 1); | ||||
|   } | ||||
| 
 | ||||
|   return state.update('items', list => list.unshift(notificationToMap(notification))); | ||||
|   return state.update('items', list => { | ||||
|     if (top && list.size > 40) { | ||||
|       list = list.take(20); | ||||
|     } | ||||
| 
 | ||||
|     return list.unshift(notificationToMap(notification)); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const normalizeNotifications = (state, notifications, next) => { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue