Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | 1x 244x 244x 244x 244x 244x 244x 244x 54x 244x 40x 244x 244x 244x 244x 244x 194x 194x 285x 285x 285x 285x 285x 285x 253x 253x 61x 253x 253x 253x 426x 1x 2x 137x 133x 133x 133x 133x 4x 4x 4x 4x 179x 448x 16x 432x 432x 174x 174x 1x 254x 158x 20x 12x 6x 12x 10x 10x 20x | import { Injectable, OnDestroy } from '@angular/core'; import { ComponentWithText } from 'src/app/interfaces/ComponentWithText'; import { WindowScrollService } from '../windowScrollService/window-scroll.service'; import { WindowResizeService } from '../windowResizeService/window-resize.service'; import { Subscription } from 'rxjs'; import { DOMComputationService } from '../domcomputation/domcomputation.service'; import { debounce } from 'src/scripts/tools/debounce/debounce'; /** * Service responsible for managing {@link ComponentWithText} so that only * components in the viewport actually make the API calls to get the texts. To * avoid a somewhat disturbing user experience of having components loading * while scrolling or resizing, the loaded zone is actually a little bigger than * the viewport. */ @Injectable({ providedIn: 'root', }) export class VisibleToLoadTextService implements OnDestroy { /** * {@link ComponentWithText} managed. The visible components will load their * texts when appropriate. */ subscribers: ComponentWithText[] = []; /** Map containing the visibility of each {@link ComponentWithText}. */ visibility: Map<ComponentWithText, boolean>; /** * Map containing whether or not each {@link ComponentWithText} as loaded their * texts. This is important so that the {@link ComponentWithText} do not reload * their texts after it already has been done, and so that preloaders can be * displayed while the texts are loading. */ loaded: Map<ComponentWithText, boolean>; /** * Map containing whether or not each {@link ComponentWithText} is currently * loading. This is important so that the {@link ComponentWithText} do not * reload their texts when they are already loading. */ loading: Map<ComponentWithText, boolean>; /** * Map containing whether or not each {@link ComponentWithText} should reload. * Usefull for instance when a language change occurs while a component is * already loading. */ toReload: Map<ComponentWithText, boolean>; /** * Allow any {@link ComponentWithText} to be loaded when appropriate only once. * Usefull for language names for instance, which are loaded in their onw * languages and thus do not need to be reloaded on language change. */ onlyOnce: Map<ComponentWithText, boolean>; /** Scroll subscription to the {@link WindowScrollService} scroll observable */ scroll: Subscription; /** Resize subscription to the {@link WindowResizeService} resize observable */ resize: Subscription; /** * Buffer factor for the height. For instance, a buffer factor of 0 means no * buffer, a buffer factor of 1 means that the viewport height is extended * (both up and down) by another viewport height. */ bufferFactorHeight = 0.5; /** * Buffer factor for the width. For instance, a buffer factor of 0 means no * buffer, a buffer factor of 1 means that the viewport width is extended * (both left and right) by another viewport width. */ bufferFactorWidth = 0.25; /** * VisibleToLoadText service constructor * * @param windowScrollService The {@link WindowScrollService} * @param windowResizeService The {@link WindowResizeService} * @param domComputationService The {@link DOMComputationService} */ constructor( private windowScrollService: WindowScrollService, private windowResizeService: WindowResizeService, private domComputationService: DOMComputationService ) { this.scroll = windowScrollService.scroll.subscribe(() => { this.loadNewTexts(); }); this.resize = windowResizeService.resize.subscribe(() => { this.loadNewTexts(); }); this.visibility = new Map<ComponentWithText, boolean>(); this.loaded = new Map<ComponentWithText, boolean>(); this.loading = new Map<ComponentWithText, boolean>(); this.toReload = new Map<ComponentWithText, boolean>(); this.onlyOnce = new Map<ComponentWithText, boolean>(); } /** * On destroy, the service has to be unsubscribed from the scroll observable * from the {@link WindowScrollService} and the resize observable from the * {@link WindowResizeService}. */ ngOnDestroy(): void { this.scroll.unsubscribe(); this.resize.unsubscribe(); } /** * Let a {@link ComponentWithText} subscribe to this observer to be notified * when appropriate. * * @param s The {@link ComponentWithText} */ subscribe(s: ComponentWithText, onlyOnce = false) { this.subscribers.push(s); this.loaded.set(s, false); this.loading.set(s, false); this.toReload.set(s, false); this.onlyOnce.set(s, onlyOnce); this.loadNewTextsOf(s); } /** * Let a {@link ComponentWithText} unsubscribe to this observer. * * @param s The {@link ComponentWithText} */ unsubscribe(s: ComponentWithText) { const index = this.subscribers.indexOf(s); if (index > -1) { this.subscribers.splice(index, 1); } this.visibility.delete(s); this.loading.delete(s); this.loaded.delete(s); } /** * Update the visibility of a single {@link ComponentWithText} * * @param comp The {@link ComponentWithText} */ private updateVisibilityOf(comp: ComponentWithText) { this.visibility.set( comp, this.domComputationService.isIntoView( comp.getElement(), this.bufferFactorHeight, this.bufferFactorWidth ) ); } /** Update the visibility of all {@link ComponentWithText} */ updateVisibility() { for (const comp of this.subscribers) { this.updateVisibilityOf(comp); } } /** * Indicates that a {@link ComponentWithText} has finished loading the texts. * * @param comp The {@link ComponentWithText} */ textLoaded(comp: ComponentWithText) { if (!this.toReload.get(comp)) { this.loaded.set(comp, true); this.loading.set(comp, false); setTimeout(() => { this.loadNewTexts(); }, 0); } else { this.toReload.set(comp, false); this.loaded.set(comp, false); comp.updateTexts(); this.loading.set(comp, true); } } /** * Wheter or not the text of a {@link ComponentWithText} has loaded. * * @param comp The {@link ComponentWithText} * @returns Whether or not the text as loaded */ hasTextLoaded(comp: ComponentWithText) { return this.loaded.get(comp); } /** * Load new texts of a single {@link ComponentWithText} when appropriate. * * @param comp The {@link ComponentWithText} */ loadNewTextsOf(comp: ComponentWithText) { if ( (this.loading.get(comp) || this.loaded.get(comp)) && this.onlyOnce.get(comp) ) return; this.updateVisibilityOf(comp); if ( this.visibility.get(comp) && !this.loaded.get(comp) && !this.loading.get(comp) ) { comp.updateTexts(); this.loading.set(comp, true); } } /** Load new texts for all {@link ComponentWithText} when appropriate. */ @debounce() loadNewTexts() { for (const comp of this.subscribers) { this.loadNewTextsOf(comp); } } /** * On language change, reset the loaded and loading status of the * {@link ComponentWithText} since everything has to be loaded again, and then * load the new texts. */ languageChange() { for (const comp of this.subscribers) { // if the language change occurs during the loading of a component's text and // is not into view, the component should reload the texts with the correct language if ( this.loading.get(comp) && !this.loaded.get(comp) && !this.onlyOnce.get(comp) ) { this.toReload.set(comp, true); } if (!this.onlyOnce.get(comp)) { this.loading.set(comp, false); this.loaded.set(comp, false); } } this.loadNewTexts(); } } |