Merge branch 'feature/v4' of https://github.com/tidusjar/Ombi into feature/v4

pull/3798/head
tidusjar 4 years ago
commit 023246ab8c

@ -113,7 +113,8 @@ namespace Ombi.Notifications.Agents
{
fields.Add(new DiscordField { name = "Requested By", value = alias, inline = true });
}
} else
}
else
{
if (model.Data.TryGetValue("RequestedUser", out var requestedUser))
{
@ -131,7 +132,7 @@ namespace Ombi.Notifications.Agents
}
}
var color = string.Empty;
string color = null;
if (model.Data.TryGetValue("RequestStatus", out var status))
{
if (status.HasValue())

@ -45,9 +45,9 @@ export class DiscoverActorComponent implements AfterViewInit {
this.discoverResults = [];
this.actorCredits.cast.forEach(m => {
this.discoverResults.push({
available: false,
available: false,
posterPath: m.poster_path ? `https://image.tmdb.org/t/p/w300/${m.poster_path}` : "../../../images/default_movie_poster.png",
requested: false,
requested: false,
title: m.title,
type: RequestType.movie,
id: m.id,
@ -56,6 +56,7 @@ export class DiscoverActorComponent implements AfterViewInit {
overview: m.overview,
approved: false,
imdbid: "",
denied: false
});
});
}

@ -76,24 +76,24 @@
[translate]="'Common.NotRequested'"></span></ng-template>
</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.Director' | translate}}: </strong>
<small *ngIf="movie">{{movie.credits.crew[0].name}}</small>
<strong *ngIf="tvCreator">Director: </strong>
<small *ngIf="tvCreator">{{tvCreator}}</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.InCinemas' | translate}}: </strong>
<small *ngIf="movie">{{movie.releaseDate | amLocal | amDateFormat: 'LL'}}</small>
<strong *ngIf="tv">{{'Discovery.CardDetails.FirstAired' | translate}}: </strong>
<small *ngIf="tv">{{tv.firstAired | amLocal | amDateFormat: 'LL'}}</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.Writer' | translate}}: </strong>
<small *ngIf="movie">{{movie.credits.crew[1].name}}</small>
<strong *ngIf="tv">{{'Discovery.CardDetails.ExecProducer' | translate}}: </strong>
<small *ngIf="tv">{{tvProducer}}</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.Director' | translate}}: </strong>
<small *ngIf="movie">{{movie.credits.crew[0].name}}</small>
<strong *ngIf="tvCreator">Director: </strong>
<small *ngIf="tvCreator">{{tvCreator}}</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.InCinemas' | translate}}: </strong>
<small *ngIf="movie">{{movie.releaseDate | amLocal | amDateFormat: 'LL'}}</small>
<strong *ngIf="tv">{{'Discovery.CardDetails.FirstAired' | translate}}: </strong>
<small *ngIf="tv">{{tv.firstAired | amLocal | amDateFormat: 'LL'}}</small>
</div>
<div class="col-6">
<strong *ngIf="movie">{{'Discovery.CardDetails.Writer' | translate}}: </strong>
<small *ngIf="movie">{{movie.credits.crew[1].name}}</small>
<strong *ngIf="tv">{{'Discovery.CardDetails.ExecProducer' | translate}}: </strong>
<small *ngIf="tv">{{tvProducer}}</small>
</div>
</div>
<div class="row top-spacing overview">

@ -54,10 +54,11 @@ export class DiscoverCollectionsComponent implements OnInit {
overview: m.overview,
approved: m.approved,
imdbid: m.imdbId,
denied:false
});
});
}
private loading() {
this.loadingFlag = true;
}

@ -1,13 +1,23 @@
<div class="small-middle-container">
<div class="row justify-content-md-center top-space">
<div class="row justify-content-end">
<div class="btn-group col-1 small-space" role="group">
<mat-button-toggle-group *ngIf="displayOption">
<mat-button-toggle [ngClass]="displayOption === DisplayOption.Card ? 'mat-button-toggle-checked' : ''" (click)="changeView(DisplayOption.Card)"><mat-icon>dashboard</mat-icon></mat-button-toggle>
<mat-button-toggle [ngClass]="displayOption === DisplayOption.List ? 'mat-button-toggle-checked' : ''" (click)="changeView(DisplayOption.List)"><mat-icon>calendar_view_day</mat-icon></mat-button-toggle>
</mat-button-toggle-group>
</div>
</div>
<div class="row justify-content-md-center">
<div class="btn-group" role="group">
<button type="button" (click)="switchDiscoverMode(DiscoverOption.Movie)" [attr.color]="popularActive ? 'accent' : 'primary'" [ngClass]="discoverOptions === DiscoverOption.Movie ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Discovery.Movies' | translate}}</button>
<button type="button" (click)="switchDiscoverMode(DiscoverOption.Combined)" [attr.color]="trendingActive ? 'accent' : 'primary'" [ngClass]="discoverOptions === DiscoverOption.Combined ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow"
color="primary">{{'Discovery.Combined' | translate}}</button>
<button type="button" (click)="switchDiscoverMode(DiscoverOption.Tv)" [attr.color]="upcomingActive ? 'accent' : 'primary'" [ngClass]="discoverOptions === DiscoverOption.Tv ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow" color="primary">{{'Discovery.Tv' | translate}}</button>
</div>
</div>
<div class="row justify-content-md-center small-space">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" (click)="popular()" [attr.color]="popularActive ? 'accent' : 'primary'" [ngClass]="popularActive ? 'mat-accent' : 'mat-primary'" mat-raised-button class="btn grow">{{'Discovery.PopularTab' | translate}}</button>
@ -16,11 +26,16 @@
</div>
</div>
<div *ngIf="discoverResults" class="row full-height discoverResults col" infiniteScroll [fromRoot]="false" [infiniteScrollDistance]="0.5" [infiniteScrollDisabled]="scrollDisabled" (scrolled)="onScroll()">
<div *ngIf="discoverResults && displayOption === DisplayOption.Card" class="row full-height discoverResults col" infiniteScroll [fromRoot]="false" [infiniteScrollDistance]="0.5" [infiniteScrollDisabled]="scrollDisabled" (scrolled)="onScroll()">
<div class="col-xl-2 col-lg-3 col-md-4 col-12 col-sm-6 small-padding" *ngFor="let result of discoverResults">
<discover-card [result]="result"></discover-card>
</div>
</div>
<div *ngIf="discoverResults && displayOption == DisplayOption.List" class="row full-height discoverResults col" infiniteScroll [fromRoot]="true" [infiniteScrollDistance]="1" [infiniteScrollDisabled]="scrollDisabled" (scrolled)="onScroll()">
<div class="col-12 small-padding" *ngFor="let result of discoverResults">
<discover-grid [result]="result"></discover-grid>
</div>
</div>
<div *ngIf="loadingFlag" class="row justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>

@ -1,7 +1,7 @@
import { Component, OnInit, Inject } from "@angular/core";
import { SearchV2Service } from "../../../services";
import { ISearchMovieResult, ISearchTvResult, RequestType } from "../../../interfaces";
import { IDiscoverCardResult, DiscoverOption } from "../../interfaces";
import { IDiscoverCardResult, DiscoverOption, DisplayOption } from "../../interfaces";
import { trigger, transition, style, animate } from "@angular/animations";
import { StorageService } from "../../../shared/storage/storage-service";
import { DOCUMENT } from "@angular/common";
@ -26,6 +26,8 @@ export class DiscoverComponent implements OnInit {
public discoverOptions: DiscoverOption = DiscoverOption.Combined;
public DiscoverOption = DiscoverOption;
public displayOption: DisplayOption = DisplayOption.Card;
public DisplayOption = DisplayOption;
public defaultTvPoster: string;
@ -41,6 +43,7 @@ export class DiscoverComponent implements OnInit {
private contentLoaded: number;
private isScrolling: boolean = false;
private mediaTypeStorageKey = "DiscoverOptions";
private displayOptionsKey = "DiscoverDisplayOptions";
constructor(private searchService: SearchV2Service,
private storageService: StorageService,
@ -53,6 +56,10 @@ export class DiscoverComponent implements OnInit {
if (localDiscoverOptions) {
this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
}
const localDisplayOptions = +this.storageService.get(this.displayOptionsKey);
if (localDisplayOptions) {
this.displayOption = DisplayOption[DisplayOption[localDisplayOptions]];
}
this.scrollDisabled = true;
switch (this.discoverOptions) {
case DiscoverOption.Combined:
@ -221,6 +228,11 @@ export class DiscoverComponent implements OnInit {
this.finishLoading();
}
public changeView(view: DisplayOption) {
this.displayOption = view;
this.storageService.save(this.displayOptionsKey, view.toString());
}
private createModel() {
const tempResults = <IDiscoverCardResult[]>[];
@ -257,7 +269,8 @@ export class DiscoverComponent implements OnInit {
rating: m.voteAverage,
overview: m.overview,
approved: m.approved,
imdbid: m.imdbId
imdbid: m.imdbId,
denied: false
});
});
return tempResults;
@ -277,7 +290,8 @@ export class DiscoverComponent implements OnInit {
rating: +m.rating,
overview: m.overview,
approved: m.approved,
imdbid: m.imdbId
imdbid: m.imdbId,
denied: false
});
});
return tempResults;

@ -0,0 +1,136 @@
<!-- <div class="card-spacing" *ngIf="result">
<mat-card class="mat-elevation-z8 dark-card grow">
<a [routerLink]="result.type === RequestType.movie ? '/details/movie/' + result.id : '/details/tv/' + result.id">
<img id="cardImage" mat-card-image src="{{result.posterPath}}" class="card-poster" [ngClass]="getStatusClass()" alt="{{result.title}}">
</a>
<mat-card-content>
<h6 *ngIf="result.title.length <= 20">{{result.title}}</h6>
<h6 *ngIf="result.title.length > 20" matTooltip="{{result.title}}">{{result.title | truncate:20}}</h6>
<div class="fade-text">
<small class="overview-text">{{result.overview | truncate: 75}}</small>
</div>
</mat-card-content>
</mat-card>
</div> -->
<div class="top-spacing">
<mat-card class="mat-elevation-z8 dark-card">
<div class="row main-container">
<div class="col-2">
<img src="{{result.posterPath}}" class="card-poster" alt="{{result.title}}">
</div>
<div class="col-8">
<div class="row">
<h1>{{result.title}}</h1>
</div>
<div class="row">
<mat-chip-list>
<mat-chip *ngIf="result.available" class="available">
{{'Common.Available' | translate}}
</mat-chip>
<mat-chip *ngIf="result.approved && !result.available" class="approved">
{{'Common.ProcessingRequest' | translate}}
</mat-chip>
<mat-chip *ngIf="result.denied" class="denied">
{{'Common.RequestDenied' | translate}}
</mat-chip>
<mat-chip *ngIf="!result.approved && !result.available && !result.denied" class="requested">
{{'Common.PendingApproval' | translate}}
</mat-chip>
<mat-chip *ngIf="movie && movie.plexUrl"> <a href="{{movie.plexUrl}}" target="_blank"><mat-icon style="color:white" matTooltip=" {{'Search.ViewOnPlex' | translate}}"
>play_circle_outline</mat-icon></a></mat-chip>
<mat-chip *ngIf="movie && movie.embyUrl"> <a href="{{movie.embyUrl}}" target="_blank"><mat-icon style="color:white" matTooltip=" {{'Search.ViewOnEmby' | translate}}"
>play_circle_outline</mat-icon></a></mat-chip>
<mat-chip *ngIf="tv && tv.plexUrl"> <a href="{{tv.plexUrl}}" target="_blank"><mat-icon style="color:white" matTooltip=" {{'Search.ViewOnPlex' | translate}}"
>play_circle_outline</mat-icon></a></mat-chip>
<mat-chip *ngIf="tv &&tv.embyUrl"> <a href="{{movie.embyUrl}}" target="_blank"><mat-icon style="color:white" matTooltip=" {{'Search.ViewOnEmby' | translate}}"
>play_circle_outline</mat-icon></a></mat-chip>
</mat-chip-list>
</div>
<div class="row">
<mat-chip-list class="top-spacing">
<mat-chip *ngIf="movie && movie.productionCompanies[0]?.name">{{'Discovery.CardDetails.Studio' | translate}}: {{movie.productionCompanies[0].name}}</mat-chip>
<mat-chip *ngIf="tv && tv.network?.name">{{'Discovery.CardDetails.Network' | translate}}: {{tv.network.name}}</mat-chip>
<mat-chip *ngIf="movie && movie.credits?.crew[0]?.name">{{'Discovery.CardDetails.Director' | translate}}: {{movie.credits.crew[0].name}}</mat-chip>
<mat-chip *ngIf="tvCreator">Director: {{tvCreator}}</mat-chip>
<mat-chip *ngIf="movie">{{'Discovery.CardDetails.InCinemas' | translate}}: {{movie.releaseDate | amLocal | amDateFormat: 'LL'}}</mat-chip>
<mat-chip *ngIf="tv">{{'Discovery.CardDetails.FirstAired' | translate}}: {{tv.firstAired | amLocal | amDateFormat: 'LL'}}</mat-chip>
<mat-chip *ngIf="movie && movie.credits?.crew[1]?.name">{{'Discovery.CardDetails.Writer' | translate}}: {{movie.credits.crew[1].name}}</mat-chip>
<mat-chip *ngIf="tv">{{'Discovery.CardDetails.ExecProducer' | translate}}: {{tvProducer}}</mat-chip>
</mat-chip-list>
</div>
<div class="row">
<p class="overview top-spacing">{{result.overview}}</p>
</div>
</div>
<div class="col-2" >
<div style="float:right;">
<button mat-raised-button class="btn-green btn-spacing" (click)="openDetails()"> {{
'Common.ViewDetails' | translate }}</button>
<div *ngIf="movie">
<button mat-raised-button class="btn-green btn-spacing" *ngIf="movie.available"> {{
'Common.Available' | translate }}</button>
<span *ngIf="!movie.available">
<span *ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
<ng-template #requestedBtn>
<button mat-raised-button class="btn-spacing btn-orange" [disabled]><i class="fa fa-check"></i>
{{ 'Common.Requested' | translate }}</button>
</ng-template>
<ng-template #notRequestedBtn>
<button mat-raised-button class="btn-spacing" color="primary" (click)="request()">
<i *ngIf="movie.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i
*ngIf="!movie.requestProcessing && !movie.processed" class="fa fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fa fa-check"></i> {{
'Common.Request' | translate }}</button>
</ng-template>
</span>
</div>
<div *ngIf="tv">
<div *ngIf="!tv.fullyAvailable" class="dropdown">
<button mat-raised-button class="btn-orange btn-spacing" type="button" (click)="request()">
<i class="fa fa-plus"></i>
{{ 'Common.Request' | translate }}
<span class="caret"></span>
</button>
</div>
<button *ngIf="tv.fullyAvailable" mat-raised-button class="btn-spacing" color="accent" [disabled]>
<i class="fa fa-check"></i> {{'Common.Available' | translate }}</button>
<button *ngIf="tv.partlyAvailable && !tv.fullyAvailable" mat-raised-button class="btn-spacing" color="accent"
[disabled]>
<i class="fa fa-check"></i> {{'Common.PartiallyAvailable' | translate }}</button>
<span *ngIf="tv.available">
<a *ngIf="tv.plexUrl" mat-raised-button style="text-align: right" class="btn-spacing btn-greem"
href="{{tv.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' |
translate}}</a>
<a *ngIf="tv.embyUrl" mat-raised-button class="btn-green btn-spacing" href="{{tv.embyUrl}}"
target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
translate}}</a>
</span>
</div>
</div>
</div>
</div>
</mat-card>
</div>

@ -0,0 +1,132 @@
$ombi-primary:#3f3f3f;
$card-background: #2b2b2b;
$blue: #1976D2;
$pink: #C2185B;
$green:#1DE9B6;
$orange:#F57C00;
.btn-blue {
background-color: $blue;
}
.btn-pink {
background-color: $pink;
}
.btn-green {
background-color: $green;
}
.btn-orange {
background-color: $orange;
}
.btn-spacing {
margin-top:10%;
}
#cardImage {
border-radius: 5px 5px 0px 0px;
height: 75%;
}
.dark-card {
border-radius: 8px;
}
// Changed height to 100% to make all cards the same height
.top-spacing {
margin-top: 1%;
}
.card-poster {
width: 100%;
border-radius: 8px 0px 0px 8px;
margin-top: -6.5%;
margin-bottom: -6.6%;
}
.main-container {
margin-left: -2%;
}
.rating {
position: absolute;
font-weight: bold;
}
$border-width: 3px;
.available {
background-color: #1DE9B6 !important;
color: black !important;
}
.approved {
background-color: #ff5722 !important;
}
.requested {
background-color: #ffd740 !important;
color: black !important;
}
.denied {
background-color: #C2185B !important;
}
.notrequested {
background-color: #303030 !important;
}
.expand {
text-align: center;
}
@media (min-width: 1025px) {
// Changed height to 100% to make all cards the same height
.grow {
transition: all .2s ease-in-out;
height: 100%;
}
.grow:hover {
transform: scale(1.1);
}
}
::ng-deep mat-dialog-container.mat-dialog-container {
// background-color: $ombi-primary;
// color: white;
border-radius: 2%
}
/* Title adjust for the Discover page */
.mat-card-content h6 {
overflow: hidden;
white-space: nowrap;
font-weight: 400;
font-size: 1.1rem;
}
/* Summary adjust for Discover page */
.small,
small {
font-size: 0.8rem;
}
@media (min-width: 2000px) {
#cardImage {
height: 80%;
object-fit: cover;
display: block;
}
}
.overview {
font-size: 1.2em;
}

@ -0,0 +1,134 @@
import { Component, OnInit, Input } from "@angular/core";
import { IDiscoverCardResult } from "../../interfaces";
import { RequestType, ISearchTvResult, ISearchMovieResult, ISearchMovieResultContainer } from "../../../interfaces";
import { RequestService, SearchV2Service } from "../../../services";
import { MatDialog } from "@angular/material/dialog";
import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2";
import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component";
import { MatSnackBar } from "@angular/material/snack-bar";
import { Router } from "@angular/router";
@Component({
selector: "discover-grid",
templateUrl: "./discover-grid.component.html",
styleUrls: ["./discover-grid.component.scss"],
})
export class DiscoverGridComponent implements OnInit {
@Input() public result: IDiscoverCardResult;
public RequestType = RequestType;
public requesting: boolean;
public tv: ISearchTvResultV2;
public tvCreator: string;
public tvProducer: string;
public movie: ISearchMovieResultV2;
constructor(private searchService: SearchV2Service, private dialog: MatDialog,
private requestService: RequestService, private notification: MatSnackBar,
private router: Router) { }
public ngOnInit() {
if (this.result.type == RequestType.tvShow) {
this.getExtraTvInfo();
}
if (this.result.type == RequestType.movie) {
this.getExtraMovieInfo();
}
}
public async getExtraTvInfo() {
this.tv = await this.searchService.getTvInfo(this.result.id);
this.setTvDefaults(this.tv);
this.updateTvItem(this.tv);
const creator = this.tv.crew.filter(tv => {
return tv.type === "Creator";
})[0];
if (creator && creator.person) {
this.tvCreator = creator.person.name;
}
const crewResult = this.tv.crew.filter(tv => {
return tv.type === "Executive Producer";
})[0]
if (crewResult && crewResult.person) {
this.tvProducer = crewResult.person.name;
}
}
public openDetails() {
if (this.result.type === RequestType.movie) {
this.router.navigate(['/details/movie/', this.result.id]);
} else if (this.result.type === RequestType.tvShow) {
this.router.navigate(['/details/tv/', this.result.id]);
}
}
public getStatusClass(): string {
if (this.result.available) {
return "available";
}
if (this.result.approved) {
return "approved";
}
if (this.result.requested) {
return "requested";
}
return "notrequested";
}
private getExtraMovieInfo() {
// if (!this.result.imdbid) {
this.searchService.getFullMovieDetails(this.result.id)
.subscribe(m => {
this.movie = m;
this.updateMovieItem(m);
});
// }
}
private updateMovieItem(updated: ISearchMovieResultV2) {
this.result.url = "http://www.imdb.com/title/" + updated.imdbId + "/";
this.result.available = updated.available;
this.result.requested = updated.requested;
this.result.requested = updated.requestProcessing;
this.result.rating = updated.voteAverage;
}
private setTvDefaults(x: ISearchTvResultV2) {
if (!x.imdbId) {
x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId;
} else {
x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/";
}
}
private updateTvItem(updated: ISearchTvResultV2) {
this.result.title = updated.title;
this.result.id = updated.id;
this.result.available = updated.fullyAvailable;
this.result.posterPath = updated.banner;
this.result.requested = updated.requested;
this.result.url = updated.imdbId;
}
public async request() {
this.requesting = true;
if (this.result.type === RequestType.movie) {
const result = await this.requestService.requestMovie({ theMovieDbId: this.result.id, languageCode: "" }).toPromise();
if (result.result) {
this.result.requested = true;
this.notification.open(result.message, "Ok");
} else {
this.notification.open(result.errorMessage, "Ok");
}
} else if (this.result.type === RequestType.tvShow) {
this.dialog.open(EpisodeRequestComponent, { width: "700px", data: this.tv, panelClass: 'modal-panel' })
}
this.requesting = false;
}
}

@ -7,6 +7,7 @@ import { Routes } from "@angular/router";
import { AuthGuard } from "../../auth/auth.guard";
import { SearchService, RequestService } from "../../services";
import { MatDialog } from "@angular/material/dialog";
import { DiscoverGridComponent } from "./grid/discover-grid.component";
export const components: any[] = [
@ -15,6 +16,7 @@ export const components: any[] = [
DiscoverCardDetailsComponent,
DiscoverCollectionsComponent,
DiscoverActorComponent,
DiscoverGridComponent,
];

@ -1,6 +1,7 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import {MatButtonToggleModule} from '@angular/material/button-toggle';
import { SharedModule } from "../shared/shared.module";
import { PipeModule } from "../pipes/pipe.module";
@ -13,6 +14,7 @@ import * as fromComponents from './components';
RouterModule.forChild(fromComponents.routes),
SharedModule,
PipeModule,
MatButtonToggleModule,
InfiniteScrollModule,
],
declarations: [

@ -8,6 +8,7 @@ export interface IDiscoverCardResult {
type: RequestType;
available: boolean;
approved: boolean;
denied: boolean;
requested: boolean;
rating: number;
overview: string;
@ -19,3 +20,8 @@ export enum DiscoverOption {
Movie = 2,
Tv = 3
}
export enum DisplayOption {
Card = 1,
List = 2
}

@ -57,7 +57,7 @@
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
</head>
<body class="mat-typography">
<body class="mat-typography custom-background">
<app-ombi>
<script type="text/javascript">

@ -44,6 +44,10 @@ body {
-webkit-overflow-scrolling: touch;
}
.custom-background {
background-color: $background-dark !important;
}
.spinner-container {
position: relative;
margin-left: 50%;

Loading…
Cancel
Save