mirror of https://github.com/Chocobozzz/PeerTube
Add instance banner on login page
parent
cbfe10a43e
commit
93f9677463
|
@ -2,7 +2,6 @@ import { SelectOptionsItem } from 'src/types/select-options-item.model'
|
||||||
import { Component, Input, OnInit } from '@angular/core'
|
import { Component, Input, OnInit } from '@angular/core'
|
||||||
import { FormGroup } from '@angular/forms'
|
import { FormGroup } from '@angular/forms'
|
||||||
import { CustomMarkupService } from '@app/shared/shared-custom-markup'
|
import { CustomMarkupService } from '@app/shared/shared-custom-markup'
|
||||||
import { ConfigService } from '../shared/config.service'
|
|
||||||
import { Notifier } from '@app/core'
|
import { Notifier } from '@app/core'
|
||||||
import { HttpErrorResponse } from '@angular/common/http'
|
import { HttpErrorResponse } from '@angular/common/http'
|
||||||
import { genericUploadErrorHandler } from '@app/helpers'
|
import { genericUploadErrorHandler } from '@app/helpers'
|
||||||
|
@ -24,9 +23,8 @@ export class EditInstanceInformationComponent implements OnInit {
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private customMarkup: CustomMarkupService,
|
private customMarkup: CustomMarkupService,
|
||||||
private configService: ConfigService,
|
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private instance: InstanceService
|
private instanceService: InstanceService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,9 +38,9 @@ export class EditInstanceInformationComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBannerChange (formData: FormData) {
|
onBannerChange (formData: FormData) {
|
||||||
this.configService.updateInstanceBanner(formData)
|
this.instanceService.updateInstanceBanner(formData)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: data => {
|
next: () => {
|
||||||
this.notifier.success($localize`Banner changed.`)
|
this.notifier.success($localize`Banner changed.`)
|
||||||
|
|
||||||
this.resetBannerUrl()
|
this.resetBannerUrl()
|
||||||
|
@ -53,7 +51,7 @@ export class EditInstanceInformationComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBannerDelete () {
|
onBannerDelete () {
|
||||||
this.configService.deleteInstanceBanner()
|
this.instanceService.deleteInstanceBanner()
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
this.notifier.success($localize`Banner deleted.`)
|
this.notifier.success($localize`Banner deleted.`)
|
||||||
|
@ -66,15 +64,9 @@ export class EditInstanceInformationComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetBannerUrl () {
|
private resetBannerUrl () {
|
||||||
this.instance.getAbout()
|
this.instanceService.getInstanceBannerUrl()
|
||||||
.subscribe(about => {
|
.subscribe(instanceBannerUrl => {
|
||||||
const banners = about.instance.banners
|
this.instanceBannerUrl = instanceBannerUrl
|
||||||
if (banners.length === 0) {
|
|
||||||
this.instanceBannerUrl = undefined
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.instanceBannerUrl = banners[0].path
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,20 +67,4 @@ export class ConfigService {
|
||||||
return this.authHttp.put<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom', data)
|
return this.authHttp.put<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom', data)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
updateInstanceBanner (formData: FormData) {
|
|
||||||
const url = ConfigService.BASE_APPLICATION_URL + '/instance-banner/pick'
|
|
||||||
|
|
||||||
return this.authHttp.post(url, formData)
|
|
||||||
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteInstanceBanner () {
|
|
||||||
const url = ConfigService.BASE_APPLICATION_URL + '/instance-banner'
|
|
||||||
|
|
||||||
return this.authHttp.delete(url)
|
|
||||||
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0">
|
<div class="external-login-blocks" *ngIf="hasExternalLogins()">
|
||||||
<div class="fw-semibold" i18n>Or sign in with</div>
|
<div class="fw-semibold" i18n>Or sign in with</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -107,6 +107,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #instanceInformation class="instance-information">
|
<div #instanceInformation class="instance-information">
|
||||||
|
<my-instance-banner class="rounded"></my-instance-banner>
|
||||||
|
|
||||||
<my-instance-about-accordion
|
<my-instance-about-accordion
|
||||||
#instanceAboutAccordion
|
#instanceAboutAccordion
|
||||||
[displayInstanceName]="false"
|
[displayInstanceName]="false"
|
||||||
|
|
|
@ -32,6 +32,8 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
|
||||||
externalAuthError = false
|
externalAuthError = false
|
||||||
externalLogins: string[] = []
|
externalLogins: string[] = []
|
||||||
|
|
||||||
|
instanceBannerUrl: string
|
||||||
|
|
||||||
instanceInformationPanels = {
|
instanceInformationPanels = {
|
||||||
terms: true,
|
terms: true,
|
||||||
administrators: false,
|
administrators: false,
|
||||||
|
@ -120,6 +122,10 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
|
||||||
return this.serverConfig.plugin.registeredExternalAuths
|
return this.serverConfig.plugin.registeredExternalAuths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasExternalLogins () {
|
||||||
|
return this.getExternalLogins().length !== 0
|
||||||
|
}
|
||||||
|
|
||||||
getAuthHref (auth: RegisteredExternalAuthConfig) {
|
getAuthHref (auth: RegisteredExternalAuthConfig) {
|
||||||
return getExternalAuthHref(environment.apiUrl, auth)
|
return getExternalAuthHref(environment.apiUrl, auth)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { firstValueFrom } from 'rxjs'
|
|
||||||
import { ComponentRef, Injectable } from '@angular/core'
|
import { ComponentRef, Injectable } from '@angular/core'
|
||||||
import { MarkdownService } from '@app/core'
|
import { MarkdownService } from '@app/core'
|
||||||
import { logger } from '@root-helpers/logger'
|
import { logger } from '@root-helpers/logger'
|
||||||
|
@ -24,7 +23,7 @@ import {
|
||||||
} from './peertube-custom-tags'
|
} from './peertube-custom-tags'
|
||||||
import { CustomMarkupComponent } from './peertube-custom-tags/shared'
|
import { CustomMarkupComponent } from './peertube-custom-tags/shared'
|
||||||
|
|
||||||
type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<CustomMarkupComponent>
|
type AngularBuilderFunction = (el: HTMLElement) => { component: ComponentRef<CustomMarkupComponent>, loadedPromise: Promise<boolean> }
|
||||||
type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
|
type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -85,12 +84,8 @@ export class CustomMarkupService {
|
||||||
rootElement.querySelectorAll(selector)
|
rootElement.querySelectorAll(selector)
|
||||||
.forEach((e: HTMLElement) => {
|
.forEach((e: HTMLElement) => {
|
||||||
try {
|
try {
|
||||||
const component = this.execAngularBuilder(selector, e)
|
const { component, loadedPromise } = this.execAngularBuilder(selector, e)
|
||||||
|
if (loadedPromise) loadedPromises.push(loadedPromise)
|
||||||
if (component.instance.loaded) {
|
|
||||||
const p = firstValueFrom(component.instance.loaded)
|
|
||||||
loadedPromises.push(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicElementService.injectElement(e, component)
|
this.dynamicElementService.injectElement(e, component)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -117,25 +112,25 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
private embedBuilder (el: HTMLElement, type: 'video' | 'playlist') {
|
private embedBuilder (el: HTMLElement, type: 'video' | 'playlist') {
|
||||||
const data = el.dataset as EmbedMarkupData
|
const data = el.dataset as EmbedMarkupData
|
||||||
const component = this.dynamicElementService.createElement(EmbedMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(EmbedMarkupComponent)
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, { uuid: data.uuid, type })
|
this.dynamicElementService.setModel(component, { uuid: data.uuid, type })
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private playlistMiniatureBuilder (el: HTMLElement) {
|
private playlistMiniatureBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as PlaylistMiniatureMarkupData
|
const data = el.dataset as PlaylistMiniatureMarkupData
|
||||||
const component = this.dynamicElementService.createElement(PlaylistMiniatureMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(PlaylistMiniatureMarkupComponent)
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, { uuid: data.uuid })
|
this.dynamicElementService.setModel(component, { uuid: data.uuid })
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private channelMiniatureBuilder (el: HTMLElement) {
|
private channelMiniatureBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as ChannelMiniatureMarkupData
|
const data = el.dataset as ChannelMiniatureMarkupData
|
||||||
const component = this.dynamicElementService.createElement(ChannelMiniatureMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(ChannelMiniatureMarkupComponent)
|
||||||
|
|
||||||
const model = {
|
const model = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
@ -145,12 +140,12 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, model)
|
this.dynamicElementService.setModel(component, model)
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private buttonBuilder (el: HTMLElement) {
|
private buttonBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as ButtonMarkupData
|
const data = el.dataset as ButtonMarkupData
|
||||||
const component = this.dynamicElementService.createElement(ButtonMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(ButtonMarkupComponent)
|
||||||
|
|
||||||
const model = {
|
const model = {
|
||||||
theme: data.theme ?? 'primary',
|
theme: data.theme ?? 'primary',
|
||||||
|
@ -160,12 +155,12 @@ export class CustomMarkupService {
|
||||||
}
|
}
|
||||||
this.dynamicElementService.setModel(component, model)
|
this.dynamicElementService.setModel(component, model)
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private instanceBannerBuilder (el: HTMLElement) {
|
private instanceBannerBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as InstanceBannerMarkupData
|
const data = el.dataset as InstanceBannerMarkupData
|
||||||
const component = this.dynamicElementService.createElement(InstanceBannerMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(InstanceBannerMarkupComponent)
|
||||||
|
|
||||||
const model = {
|
const model = {
|
||||||
revertHomePaddingTop: this.buildBoolean(data.revertHomePaddingTop) ?? true
|
revertHomePaddingTop: this.buildBoolean(data.revertHomePaddingTop) ?? true
|
||||||
|
@ -173,12 +168,12 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, model)
|
this.dynamicElementService.setModel(component, model)
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private videoMiniatureBuilder (el: HTMLElement) {
|
private videoMiniatureBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as VideoMiniatureMarkupData
|
const data = el.dataset as VideoMiniatureMarkupData
|
||||||
const component = this.dynamicElementService.createElement(VideoMiniatureMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(VideoMiniatureMarkupComponent)
|
||||||
|
|
||||||
const model = {
|
const model = {
|
||||||
uuid: data.uuid,
|
uuid: data.uuid,
|
||||||
|
@ -187,12 +182,12 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, model)
|
this.dynamicElementService.setModel(component, model)
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private videosListBuilder (el: HTMLElement) {
|
private videosListBuilder (el: HTMLElement) {
|
||||||
const data = el.dataset as VideosListMarkupData
|
const data = el.dataset as VideosListMarkupData
|
||||||
const component = this.dynamicElementService.createElement(VideosListMarkupComponent)
|
const { component, loadedPromise } = this.dynamicElementService.createElement(VideosListMarkupComponent)
|
||||||
|
|
||||||
const model = {
|
const model = {
|
||||||
onlyDisplayTitle: this.buildBoolean(data.onlyDisplayTitle) ?? false,
|
onlyDisplayTitle: this.buildBoolean(data.onlyDisplayTitle) ?? false,
|
||||||
|
@ -214,7 +209,7 @@ export class CustomMarkupService {
|
||||||
|
|
||||||
this.dynamicElementService.setModel(component, model)
|
this.dynamicElementService.setModel(component, model)
|
||||||
|
|
||||||
return component
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
private containerBuilder (el: HTMLElement) {
|
private containerBuilder (el: HTMLElement) {
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {
|
||||||
Type
|
Type
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { objectKeysTyped } from '@peertube/peertube-core-utils'
|
import { objectKeysTyped } from '@peertube/peertube-core-utils'
|
||||||
|
import { CustomMarkupComponent } from './peertube-custom-tags/shared'
|
||||||
|
import { firstValueFrom } from 'rxjs'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DynamicElementService {
|
export class DynamicElementService {
|
||||||
|
@ -20,7 +22,7 @@ export class DynamicElementService {
|
||||||
private applicationRef: ApplicationRef
|
private applicationRef: ApplicationRef
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
createElement <T> (ofComponent: Type<T>) {
|
createElement <T extends CustomMarkupComponent> (ofComponent: Type<T>) {
|
||||||
const div = document.createElement('div')
|
const div = document.createElement('div')
|
||||||
|
|
||||||
const component = createComponent(ofComponent, {
|
const component = createComponent(ofComponent, {
|
||||||
|
@ -29,7 +31,11 @@ export class DynamicElementService {
|
||||||
hostElement: div
|
hostElement: div
|
||||||
})
|
})
|
||||||
|
|
||||||
return component
|
const loadedPromise = component.instance.loaded
|
||||||
|
? firstValueFrom(component.instance.loaded)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return { component, loadedPromise }
|
||||||
}
|
}
|
||||||
|
|
||||||
injectElement <T> (wrapper: HTMLElement, componentRef: ComponentRef<T>) {
|
injectElement <T> (wrapper: HTMLElement, componentRef: ComponentRef<T>) {
|
||||||
|
|
|
@ -25,12 +25,10 @@ export class InstanceBannerMarkupComponent implements OnInit, CustomMarkupCompon
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.instance.getAbout()
|
this.instance.getInstanceBannerUrl()
|
||||||
.pipe(finalize(() => this.loaded.emit(true)))
|
.pipe(finalize(() => this.loaded.emit(true)))
|
||||||
.subscribe(about => {
|
.subscribe(instanceBannerUrl => {
|
||||||
if (about.instance.banners.length === 0) return
|
this.instanceBannerUrl = instanceBannerUrl
|
||||||
|
|
||||||
this.instanceBannerUrl = about.instance.banners[0].path
|
|
||||||
this.cd.markForCheck()
|
this.cd.markForCheck()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export * from './feature-boolean.component'
|
export * from './feature-boolean.component'
|
||||||
export * from './instance-about-accordion.component'
|
export * from './instance-about-accordion.component'
|
||||||
|
export * from './instance-banner.component'
|
||||||
export * from './instance-features-table.component'
|
export * from './instance-features-table.component'
|
||||||
export * from './instance-follow.service'
|
export * from './instance-follow.service'
|
||||||
export * from './instance.service'
|
export * from './instance.service'
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="banner" [ngClass]="{ rounded }">
|
||||||
|
<img class="rounded" [src]="instanceBannerUrl" alt="Instance banner">
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Component, Input, OnInit, booleanAttribute } from '@angular/core'
|
||||||
|
import { InstanceService } from './instance.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-instance-banner',
|
||||||
|
templateUrl: './instance-banner.component.html'
|
||||||
|
})
|
||||||
|
export class InstanceBannerComponent implements OnInit {
|
||||||
|
@Input({ transform: booleanAttribute }) rounded: boolean
|
||||||
|
|
||||||
|
instanceBannerUrl: string
|
||||||
|
|
||||||
|
constructor (private instanceService: InstanceService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.instanceService.getInstanceBannerUrl()
|
||||||
|
.subscribe(instanceBannerUrl => this.instanceBannerUrl = instanceBannerUrl)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { forkJoin } from 'rxjs'
|
import { forkJoin, of } from 'rxjs'
|
||||||
import { catchError, map } from 'rxjs/operators'
|
import { catchError, map, tap } from 'rxjs/operators'
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { MarkdownService, RestExtractor, ServerService } from '@app/core'
|
import { MarkdownService, RestExtractor, ServerService } from '@app/core'
|
||||||
|
@ -18,6 +18,8 @@ export class InstanceService {
|
||||||
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config'
|
private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config'
|
||||||
private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server'
|
private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server'
|
||||||
|
|
||||||
|
private instanceBannerUrl: string
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private authHttp: HttpClient,
|
private authHttp: HttpClient,
|
||||||
private restExtractor: RestExtractor,
|
private restExtractor: RestExtractor,
|
||||||
|
@ -28,9 +30,46 @@ export class InstanceService {
|
||||||
|
|
||||||
getAbout () {
|
getAbout () {
|
||||||
return this.authHttp.get<About>(InstanceService.BASE_CONFIG_URL + '/about')
|
return this.authHttp.get<About>(InstanceService.BASE_CONFIG_URL + '/about')
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(
|
||||||
|
tap(about => {
|
||||||
|
const banners = about.instance.banners
|
||||||
|
if (banners.length !== 0) this.instanceBannerUrl = banners[0].path
|
||||||
|
}),
|
||||||
|
catchError(res => this.restExtractor.handleError(res))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
getInstanceBannerUrl () {
|
||||||
|
if (this.instanceBannerUrl || this.instanceBannerUrl === null) {
|
||||||
|
return of(this.instanceBannerUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getAbout()
|
||||||
|
.pipe(map(() => this.instanceBannerUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInstanceBanner (formData: FormData) {
|
||||||
|
this.instanceBannerUrl = undefined
|
||||||
|
|
||||||
|
const url = InstanceService.BASE_CONFIG_URL + '/instance-banner/pick'
|
||||||
|
|
||||||
|
return this.authHttp.post(url, formData)
|
||||||
|
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteInstanceBanner () {
|
||||||
|
this.instanceBannerUrl = null
|
||||||
|
|
||||||
|
const url = InstanceService.BASE_CONFIG_URL + '/instance-banner'
|
||||||
|
|
||||||
|
return this.authHttp.delete(url)
|
||||||
|
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
contactAdministrator (fromEmail: string, fromName: string, subject: string, message: string) {
|
contactAdministrator (fromEmail: string, fromName: string, subject: string, message: string) {
|
||||||
const body = {
|
const body = {
|
||||||
fromEmail,
|
fromEmail,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { InstanceAboutAccordionComponent } from './instance-about-accordion.comp
|
||||||
import { InstanceFeaturesTableComponent } from './instance-features-table.component'
|
import { InstanceFeaturesTableComponent } from './instance-features-table.component'
|
||||||
import { InstanceFollowService } from './instance-follow.service'
|
import { InstanceFollowService } from './instance-follow.service'
|
||||||
import { InstanceService } from './instance.service'
|
import { InstanceService } from './instance.service'
|
||||||
|
import { InstanceBannerComponent } from './instance-banner.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -18,13 +19,15 @@ import { InstanceService } from './instance.service'
|
||||||
declarations: [
|
declarations: [
|
||||||
FeatureBooleanComponent,
|
FeatureBooleanComponent,
|
||||||
InstanceAboutAccordionComponent,
|
InstanceAboutAccordionComponent,
|
||||||
InstanceFeaturesTableComponent
|
InstanceFeaturesTableComponent,
|
||||||
|
InstanceBannerComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
FeatureBooleanComponent,
|
FeatureBooleanComponent,
|
||||||
InstanceAboutAccordionComponent,
|
InstanceAboutAccordionComponent,
|
||||||
InstanceFeaturesTableComponent
|
InstanceFeaturesTableComponent,
|
||||||
|
InstanceBannerComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
|
|
Loading…
Reference in New Issue