diff options
author | Chocobozzz <me@florianbigard.com> | 2020-08-12 10:40:04 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2020-08-14 10:28:30 +0200 |
commit | 66357162f8e1227495f09bd4f68446aad7071c6d (patch) | |
tree | 7d4429506deb512b2fe1d0267f38a28cda20af55 /client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts | |
parent | 8c360747995e17eb5520e22fc3d7bd4c3d26eeee (diff) | |
download | PeerTube-66357162f8e1227495f09bd4f68446aad7071c6d.tar.gz PeerTube-66357162f8e1227495f09bd4f68446aad7071c6d.tar.zst PeerTube-66357162f8e1227495f09bd4f68446aad7071c6d.zip |
Migrate to $localize
* Remove i18n polyfill to translate things in components
* Reduce bundle sizes
* Improve runtime perf
* Reduce a lot the time to make a full client build
* Reduce client build complexity
* We don't need a service to translate things anymore (so we will be able to translate title pages etc)
Unfortunately we may loose some translations in the migration process.
I'll put a message on weblate to notify translators
Diffstat (limited to 'client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts')
-rw-r--r-- | client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts | 103 |
1 files changed, 49 insertions, 54 deletions
diff --git a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts index 34fa7366c..44aefa853 100644 --- a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts +++ b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' | 1 | import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' |
2 | import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core' | 2 | import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core' |
3 | import { Account, DropdownAction } from '@app/shared/shared-main' | 3 | import { Account, DropdownAction } from '@app/shared/shared-main' |
4 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
5 | import { BulkRemoveCommentsOfBody, ServerConfig, User, UserRight } from '@shared/models' | 4 | import { BulkRemoveCommentsOfBody, ServerConfig, User, UserRight } from '@shared/models' |
6 | import { BlocklistService } from './blocklist.service' | 5 | import { BlocklistService } from './blocklist.service' |
7 | import { BulkService } from './bulk.service' | 6 | import { BulkService } from './bulk.service' |
@@ -37,8 +36,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
37 | private serverService: ServerService, | 36 | private serverService: ServerService, |
38 | private userService: UserService, | 37 | private userService: UserService, |
39 | private blocklistService: BlocklistService, | 38 | private blocklistService: BlocklistService, |
40 | private bulkService: BulkService, | 39 | private bulkService: BulkService |
41 | private i18n: I18n | ||
42 | ) { } | 40 | ) { } |
43 | 41 | ||
44 | get requiresEmailVerification () { | 42 | get requiresEmailVerification () { |
@@ -57,7 +55,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
57 | 55 | ||
58 | openBanUserModal (user: User) { | 56 | openBanUserModal (user: User) { |
59 | if (user.username === 'root') { | 57 | if (user.username === 'root') { |
60 | this.notifier.error(this.i18n('You cannot ban root.')) | 58 | this.notifier.error($localize`You cannot ban root.`) |
61 | return | 59 | return |
62 | } | 60 | } |
63 | 61 | ||
@@ -69,15 +67,13 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
69 | } | 67 | } |
70 | 68 | ||
71 | async unbanUser (user: User) { | 69 | async unbanUser (user: User) { |
72 | const message = this.i18n('Do you really want to unban {{username}}?', { username: user.username }) | 70 | const res = await this.confirmService.confirm($localize`Do you really want to unban ${user.username}?`, $localize`Unban`) |
73 | const res = await this.confirmService.confirm(message, this.i18n('Unban')) | ||
74 | if (res === false) return | 71 | if (res === false) return |
75 | 72 | ||
76 | this.userService.unbanUsers(user) | 73 | this.userService.unbanUsers(user) |
77 | .subscribe( | 74 | .subscribe( |
78 | () => { | 75 | () => { |
79 | this.notifier.success(this.i18n('User {{username}} unbanned.', { username: user.username })) | 76 | this.notifier.success($localize`User ${user.username} unbanned.`) |
80 | |||
81 | this.userChanged.emit() | 77 | this.userChanged.emit() |
82 | }, | 78 | }, |
83 | 79 | ||
@@ -87,17 +83,17 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
87 | 83 | ||
88 | async removeUser (user: User) { | 84 | async removeUser (user: User) { |
89 | if (user.username === 'root') { | 85 | if (user.username === 'root') { |
90 | this.notifier.error(this.i18n('You cannot delete root.')) | 86 | this.notifier.error($localize`You cannot delete root.`) |
91 | return | 87 | return |
92 | } | 88 | } |
93 | 89 | ||
94 | const message = this.i18n('If you remove this user, you will not be able to create another with the same username!') | 90 | const message = $localize`If you remove this user, you will not be able to create another with the same username!` |
95 | const res = await this.confirmService.confirm(message, this.i18n('Delete')) | 91 | const res = await this.confirmService.confirm(message, $localize`Delete`) |
96 | if (res === false) return | 92 | if (res === false) return |
97 | 93 | ||
98 | this.userService.removeUser(user).subscribe( | 94 | this.userService.removeUser(user).subscribe( |
99 | () => { | 95 | () => { |
100 | this.notifier.success(this.i18n('User {{username}} deleted.', { username: user.username })) | 96 | this.notifier.success($localize`User ${user.username} deleted.`) |
101 | this.userDeleted.emit() | 97 | this.userDeleted.emit() |
102 | }, | 98 | }, |
103 | 99 | ||
@@ -108,8 +104,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
108 | setEmailAsVerified (user: User) { | 104 | setEmailAsVerified (user: User) { |
109 | this.userService.updateUser(user.id, { emailVerified: true }).subscribe( | 105 | this.userService.updateUser(user.id, { emailVerified: true }).subscribe( |
110 | () => { | 106 | () => { |
111 | this.notifier.success(this.i18n('User {{username}} email set as verified', { username: user.username })) | 107 | this.notifier.success($localize`User ${user.username} email set as verified`) |
112 | |||
113 | this.userChanged.emit() | 108 | this.userChanged.emit() |
114 | }, | 109 | }, |
115 | 110 | ||
@@ -121,7 +116,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
121 | this.blocklistService.blockAccountByUser(account) | 116 | this.blocklistService.blockAccountByUser(account) |
122 | .subscribe( | 117 | .subscribe( |
123 | () => { | 118 | () => { |
124 | this.notifier.success(this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost })) | 119 | this.notifier.success($localize`Account ${account.nameWithHost} muted.`) |
125 | 120 | ||
126 | this.account.mutedByUser = true | 121 | this.account.mutedByUser = true |
127 | this.userChanged.emit() | 122 | this.userChanged.emit() |
@@ -135,7 +130,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
135 | this.blocklistService.unblockAccountByUser(account) | 130 | this.blocklistService.unblockAccountByUser(account) |
136 | .subscribe( | 131 | .subscribe( |
137 | () => { | 132 | () => { |
138 | this.notifier.success(this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost })) | 133 | this.notifier.success($localize`Account ${account.nameWithHost} unmuted.`) |
139 | 134 | ||
140 | this.account.mutedByUser = false | 135 | this.account.mutedByUser = false |
141 | this.userChanged.emit() | 136 | this.userChanged.emit() |
@@ -149,7 +144,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
149 | this.blocklistService.blockServerByUser(host) | 144 | this.blocklistService.blockServerByUser(host) |
150 | .subscribe( | 145 | .subscribe( |
151 | () => { | 146 | () => { |
152 | this.notifier.success(this.i18n('Instance {{host}} muted.', { host })) | 147 | this.notifier.success($localize`Instance ${host} muted.`) |
153 | 148 | ||
154 | this.account.mutedServerByUser = true | 149 | this.account.mutedServerByUser = true |
155 | this.userChanged.emit() | 150 | this.userChanged.emit() |
@@ -163,7 +158,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
163 | this.blocklistService.unblockServerByUser(host) | 158 | this.blocklistService.unblockServerByUser(host) |
164 | .subscribe( | 159 | .subscribe( |
165 | () => { | 160 | () => { |
166 | this.notifier.success(this.i18n('Instance {{host}} unmuted.', { host })) | 161 | this.notifier.success($localize`Instance ${host} unmuted.`) |
167 | 162 | ||
168 | this.account.mutedServerByUser = false | 163 | this.account.mutedServerByUser = false |
169 | this.userChanged.emit() | 164 | this.userChanged.emit() |
@@ -177,7 +172,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
177 | this.blocklistService.blockAccountByInstance(account) | 172 | this.blocklistService.blockAccountByInstance(account) |
178 | .subscribe( | 173 | .subscribe( |
179 | () => { | 174 | () => { |
180 | this.notifier.success(this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost })) | 175 | this.notifier.success($localize`Account ${account.nameWithHost} muted by the instance.`) |
181 | 176 | ||
182 | this.account.mutedByInstance = true | 177 | this.account.mutedByInstance = true |
183 | this.userChanged.emit() | 178 | this.userChanged.emit() |
@@ -191,7 +186,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
191 | this.blocklistService.unblockAccountByInstance(account) | 186 | this.blocklistService.unblockAccountByInstance(account) |
192 | .subscribe( | 187 | .subscribe( |
193 | () => { | 188 | () => { |
194 | this.notifier.success(this.i18n('Account {{nameWithHost}} unmuted by the instance.', { nameWithHost: account.nameWithHost })) | 189 | this.notifier.success($localize`Account ${account.nameWithHost} unmuted by the instance.`) |
195 | 190 | ||
196 | this.account.mutedByInstance = false | 191 | this.account.mutedByInstance = false |
197 | this.userChanged.emit() | 192 | this.userChanged.emit() |
@@ -205,7 +200,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
205 | this.blocklistService.blockServerByInstance(host) | 200 | this.blocklistService.blockServerByInstance(host) |
206 | .subscribe( | 201 | .subscribe( |
207 | () => { | 202 | () => { |
208 | this.notifier.success(this.i18n('Instance {{host}} muted by the instance.', { host })) | 203 | this.notifier.success($localize`Instance ${host} muted by the instance.`) |
209 | 204 | ||
210 | this.account.mutedServerByInstance = true | 205 | this.account.mutedServerByInstance = true |
211 | this.userChanged.emit() | 206 | this.userChanged.emit() |
@@ -219,7 +214,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
219 | this.blocklistService.unblockServerByInstance(host) | 214 | this.blocklistService.unblockServerByInstance(host) |
220 | .subscribe( | 215 | .subscribe( |
221 | () => { | 216 | () => { |
222 | this.notifier.success(this.i18n('Instance {{host}} unmuted by the instance.', { host })) | 217 | this.notifier.success($localize`Instance ${host} unmuted by the instance.`) |
223 | 218 | ||
224 | this.account.mutedServerByInstance = false | 219 | this.account.mutedServerByInstance = false |
225 | this.userChanged.emit() | 220 | this.userChanged.emit() |
@@ -230,14 +225,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
230 | } | 225 | } |
231 | 226 | ||
232 | async bulkRemoveCommentsOf (body: BulkRemoveCommentsOfBody) { | 227 | async bulkRemoveCommentsOf (body: BulkRemoveCommentsOfBody) { |
233 | const message = this.i18n('Are you sure you want to remove all the comments of this account?') | 228 | const message = $localize`Are you sure you want to remove all the comments of this account?` |
234 | const res = await this.confirmService.confirm(message, this.i18n('Delete account comments')) | 229 | const res = await this.confirmService.confirm(message, $localize`Delete account comments`) |
235 | if (res === false) return | 230 | if (res === false) return |
236 | 231 | ||
237 | this.bulkService.removeCommentsOf(body) | 232 | this.bulkService.removeCommentsOf(body) |
238 | .subscribe( | 233 | .subscribe( |
239 | () => { | 234 | () => { |
240 | this.notifier.success(this.i18n('Will remove comments of this account (may take several minutes).')) | 235 | this.notifier.success($localize`Will remove comments of this account (may take several minutes).`) |
241 | }, | 236 | }, |
242 | 237 | ||
243 | err => this.notifier.error(err.message) | 238 | err => this.notifier.error(err.message) |
@@ -265,29 +260,29 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
265 | if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { | 260 | if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { |
266 | this.userActions.push([ | 261 | this.userActions.push([ |
267 | { | 262 | { |
268 | label: this.i18n('Edit user'), | 263 | label: $localize`Edit user`, |
269 | description: this.i18n('Change quota, role, and more.'), | 264 | description: $localize`Change quota, role, and more.`, |
270 | linkBuilder: ({ user }) => this.getRouterUserEditLink(user) | 265 | linkBuilder: ({ user }) => this.getRouterUserEditLink(user) |
271 | }, | 266 | }, |
272 | { | 267 | { |
273 | label: this.i18n('Delete user'), | 268 | label: $localize`Delete user`, |
274 | description: this.i18n('Videos will be deleted, comments will be tombstoned.'), | 269 | description: $localize`Videos will be deleted, comments will be tombstoned.`, |
275 | handler: ({ user }) => this.removeUser(user) | 270 | handler: ({ user }) => this.removeUser(user) |
276 | }, | 271 | }, |
277 | { | 272 | { |
278 | label: this.i18n('Ban'), | 273 | label: $localize`Ban`, |
279 | description: this.i18n('User won\'t be able to login anymore, but videos and comments will be kept as is.'), | 274 | description: $localize`User won't be able to login anymore, but videos and comments will be kept as is.`, |
280 | handler: ({ user }) => this.openBanUserModal(user), | 275 | handler: ({ user }) => this.openBanUserModal(user), |
281 | isDisplayed: ({ user }) => !user.blocked | 276 | isDisplayed: ({ user }) => !user.blocked |
282 | }, | 277 | }, |
283 | { | 278 | { |
284 | label: this.i18n('Unban user'), | 279 | label: $localize`Unban user`, |
285 | description: this.i18n('Allow the user to login and create videos/comments again'), | 280 | description: $localize`Allow the user to login and create videos/comments again`, |
286 | handler: ({ user }) => this.unbanUser(user), | 281 | handler: ({ user }) => this.unbanUser(user), |
287 | isDisplayed: ({ user }) => user.blocked | 282 | isDisplayed: ({ user }) => user.blocked |
288 | }, | 283 | }, |
289 | { | 284 | { |
290 | label: this.i18n('Set Email as Verified'), | 285 | label: $localize`Set Email as Verified`, |
291 | handler: ({ user }) => this.setEmailAsVerified(user), | 286 | handler: ({ user }) => this.setEmailAsVerified(user), |
292 | isDisplayed: ({ user }) => this.requiresEmailVerification && !user.blocked && user.emailVerified === false | 287 | isDisplayed: ({ user }) => this.requiresEmailVerification && !user.blocked && user.emailVerified === false |
293 | } | 288 | } |
@@ -299,32 +294,32 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
299 | // User actions | 294 | // User actions |
300 | this.userActions.push([ | 295 | this.userActions.push([ |
301 | { | 296 | { |
302 | label: this.i18n('Mute this account'), | 297 | label: $localize`Mute this account`, |
303 | description: this.i18n('Hide any content from that user for you.'), | 298 | description: $localize`Hide any content from that user for you.`, |
304 | isDisplayed: ({ account }) => account.mutedByUser === false, | 299 | isDisplayed: ({ account }) => account.mutedByUser === false, |
305 | handler: ({ account }) => this.blockAccountByUser(account) | 300 | handler: ({ account }) => this.blockAccountByUser(account) |
306 | }, | 301 | }, |
307 | { | 302 | { |
308 | label: this.i18n('Unmute this account'), | 303 | label: $localize`Unmute this account`, |
309 | description: this.i18n('Show back content from that user for you.'), | 304 | description: $localize`Show back content from that user for you.`, |
310 | isDisplayed: ({ account }) => account.mutedByUser === true, | 305 | isDisplayed: ({ account }) => account.mutedByUser === true, |
311 | handler: ({ account }) => this.unblockAccountByUser(account) | 306 | handler: ({ account }) => this.unblockAccountByUser(account) |
312 | }, | 307 | }, |
313 | { | 308 | { |
314 | label: this.i18n('Mute the instance'), | 309 | label: $localize`Mute the instance`, |
315 | description: this.i18n('Hide any content from that instance for you.'), | 310 | description: $localize`Hide any content from that instance for you.`, |
316 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, | 311 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, |
317 | handler: ({ account }) => this.blockServerByUser(account.host) | 312 | handler: ({ account }) => this.blockServerByUser(account.host) |
318 | }, | 313 | }, |
319 | { | 314 | { |
320 | label: this.i18n('Unmute the instance'), | 315 | label: $localize`Unmute the instance`, |
321 | description: this.i18n('Show back content from that instance for you.'), | 316 | description: $localize`Show back content from that instance for you.`, |
322 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, | 317 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, |
323 | handler: ({ account }) => this.unblockServerByUser(account.host) | 318 | handler: ({ account }) => this.unblockServerByUser(account.host) |
324 | }, | 319 | }, |
325 | { | 320 | { |
326 | label: this.i18n('Remove comments from your videos'), | 321 | label: $localize`Remove comments from your videos`, |
327 | description: this.i18n('Remove comments of this account from your videos.'), | 322 | description: $localize`Remove comments of this account from your videos.`, |
328 | handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'my-videos' }) | 323 | handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'my-videos' }) |
329 | } | 324 | } |
330 | ]) | 325 | ]) |
@@ -335,14 +330,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
335 | if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { | 330 | if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { |
336 | instanceActions = instanceActions.concat([ | 331 | instanceActions = instanceActions.concat([ |
337 | { | 332 | { |
338 | label: this.i18n('Mute this account by your instance'), | 333 | label: $localize`Mute this account by your instance`, |
339 | description: this.i18n('Hide any content from that user for you, your instance and its users.'), | 334 | description: $localize`Hide any content from that user for you, your instance and its users.`, |
340 | isDisplayed: ({ account }) => account.mutedByInstance === false, | 335 | isDisplayed: ({ account }) => account.mutedByInstance === false, |
341 | handler: ({ account }) => this.blockAccountByInstance(account) | 336 | handler: ({ account }) => this.blockAccountByInstance(account) |
342 | }, | 337 | }, |
343 | { | 338 | { |
344 | label: this.i18n('Unmute this account by your instance'), | 339 | label: $localize`Unmute this account by your instance`, |
345 | description: this.i18n('Show back content from that user for you, your instance and its users.'), | 340 | description: $localize`Show back content from that user for you, your instance and its users.`, |
346 | isDisplayed: ({ account }) => account.mutedByInstance === true, | 341 | isDisplayed: ({ account }) => account.mutedByInstance === true, |
347 | handler: ({ account }) => this.unblockAccountByInstance(account) | 342 | handler: ({ account }) => this.unblockAccountByInstance(account) |
348 | } | 343 | } |
@@ -353,14 +348,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
353 | if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { | 348 | if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { |
354 | instanceActions = instanceActions.concat([ | 349 | instanceActions = instanceActions.concat([ |
355 | { | 350 | { |
356 | label: this.i18n('Mute the instance by your instance'), | 351 | label: $localize`Mute the instance by your instance`, |
357 | description: this.i18n('Hide any content from that instance for you, your instance and its users.'), | 352 | description: $localize`Hide any content from that instance for you, your instance and its users.`, |
358 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, | 353 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, |
359 | handler: ({ account }) => this.blockServerByInstance(account.host) | 354 | handler: ({ account }) => this.blockServerByInstance(account.host) |
360 | }, | 355 | }, |
361 | { | 356 | { |
362 | label: this.i18n('Unmute the instance by your instance'), | 357 | label: $localize`Unmute the instance by your instance`, |
363 | description: this.i18n('Show back content from that instance for you, your instance and its users.'), | 358 | description: $localize`Show back content from that instance for you, your instance and its users.`, |
364 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, | 359 | isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, |
365 | handler: ({ account }) => this.unblockServerByInstance(account.host) | 360 | handler: ({ account }) => this.unblockServerByInstance(account.host) |
366 | } | 361 | } |
@@ -370,8 +365,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges { | |||
370 | if (authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { | 365 | if (authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { |
371 | instanceActions = instanceActions.concat([ | 366 | instanceActions = instanceActions.concat([ |
372 | { | 367 | { |
373 | label: this.i18n('Remove comments from your instance'), | 368 | label: $localize`Remove comments from your instance`, |
374 | description: this.i18n('Remove comments of this account from your instance.'), | 369 | description: $localize`Remove comments of this account from your instance.`, |
375 | handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'instance' }) | 370 | handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'instance' }) |
376 | } | 371 | } |
377 | ]) | 372 | ]) |