diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0ef47812..8bc070c6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -32,6 +32,14 @@ import { AddNewAccountComponent } from './components/floating-column/add-new-acc import { SearchComponent } from './components/floating-column/search/search.component'; import { AddNewStatusComponent } from "./components/floating-column/add-new-status/add-new-status.component"; import { ManageAccountComponent } from "./components/floating-column/manage-account/manage-account.component"; +import { ActionBarComponent } from './components/stream/status/action-bar/action-bar.component'; +import { WaitingAnimationComponent } from './components/waiting-animation/waiting-animation.component'; +import { ReplyToStatusComponent } from './components/stream/status/reply-to-status/reply-to-status.component'; +import { UserProfileComponent } from './components/stream/user-profile/user-profile.component'; +import { ThreadComponent } from './components/stream/thread/thread.component'; +import { HashtagComponent } from './components/stream/hashtag/hashtag.component'; +import { StreamOverlayComponent } from './components/stream/stream-overlay/stream-overlay.component'; +import { DatabindedTextComponent } from './components/stream/status/databinded-text/databinded-text.component'; const routes: Routes = [ { path: "", redirectTo: "home", pathMatch: "full" }, @@ -56,7 +64,15 @@ const routes: Routes = [ AttachementsComponent, SettingsComponent, AddNewAccountComponent, - SearchComponent + SearchComponent, + ActionBarComponent, + WaitingAnimationComponent, + ReplyToStatusComponent, + UserProfileComponent, + ThreadComponent, + HashtagComponent, + StreamOverlayComponent, + DatabindedTextComponent ], imports: [ BrowserModule, diff --git a/src/app/components/floating-column/add-new-status/add-new-status.component.html b/src/app/components/floating-column/add-new-status/add-new-status.component.html index 52e1afa4..ebcca85c 100644 --- a/src/app/components/floating-column/add-new-status/add-new-status.component.html +++ b/src/app/components/floating-column/add-new-status/add-new-status.component.html @@ -3,10 +3,9 @@
- + - + + placeholder="Search" autocomplete="off" />
+ +

Accounts

- + -
+

Statuses

-
+

Hashtags

#{{ hashtag }} - +
\ No newline at end of file diff --git a/src/app/components/floating-column/search/search.component.scss b/src/app/components/floating-column/search/search.component.scss index 40e04779..3fba63fc 100644 --- a/src/app/components/floating-column/search/search.component.scss +++ b/src/app/components/floating-column/search/search.component.scss @@ -1,6 +1,7 @@ @import "variables"; @import "mixins"; @import "panel"; +@import "commons"; $separator-color:$color-primary; diff --git a/src/app/components/floating-column/search/search.component.ts b/src/app/components/floating-column/search/search.component.ts index b20db1ef..21f65ae6 100644 --- a/src/app/components/floating-column/search/search.component.ts +++ b/src/app/components/floating-column/search/search.component.ts @@ -18,6 +18,8 @@ export class SearchComponent implements OnInit { statuses: Status[] = []; hashtags: string[] = []; + isLoading: boolean; + constructor( private readonly store: Store, private readonly mastodonService: MastodonService) { } @@ -35,6 +37,7 @@ export class SearchComponent implements OnInit { this.accounts.length = 0; this.statuses.length = 0; this.hashtags.length = 0; + this.isLoading = true; console.warn(`search: ${data}`); @@ -57,7 +60,8 @@ export class SearchComponent implements OnInit { } } }) - .catch((err) => console.error(err)); + .catch((err) => console.error(err)) + .then(() => { this.isLoading = false; }); } } diff --git a/src/app/components/stream/hashtag/hashtag.component.html b/src/app/components/stream/hashtag/hashtag.component.html new file mode 100644 index 00000000..d58c9b76 --- /dev/null +++ b/src/app/components/stream/hashtag/hashtag.component.html @@ -0,0 +1,3 @@ +

+ hashtag works! +

diff --git a/src/app/components/stream/hashtag/hashtag.component.scss b/src/app/components/stream/hashtag/hashtag.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/stream/hashtag/hashtag.component.spec.ts b/src/app/components/stream/hashtag/hashtag.component.spec.ts new file mode 100644 index 00000000..5ee70649 --- /dev/null +++ b/src/app/components/stream/hashtag/hashtag.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HashtagComponent } from './hashtag.component'; + +describe('HashtagComponent', () => { + let component: HashtagComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HashtagComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HashtagComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/hashtag/hashtag.component.ts b/src/app/components/stream/hashtag/hashtag.component.ts new file mode 100644 index 00000000..3cb9cf38 --- /dev/null +++ b/src/app/components/stream/hashtag/hashtag.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-hashtag', + templateUrl: './hashtag.component.html', + styleUrls: ['./hashtag.component.scss'] +}) +export class HashtagComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/components/stream/status/action-bar/action-bar.component.html b/src/app/components/stream/status/action-bar/action-bar.component.html new file mode 100644 index 00000000..2f50011b --- /dev/null +++ b/src/app/components/stream/status/action-bar/action-bar.component.html @@ -0,0 +1,21 @@ +
+ + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/app/components/stream/status/action-bar/action-bar.component.scss b/src/app/components/stream/status/action-bar/action-bar.component.scss new file mode 100644 index 00000000..ae753be3 --- /dev/null +++ b/src/app/components/stream/status/action-bar/action-bar.component.scss @@ -0,0 +1,42 @@ +@import "variables"; +.action-bar { + // outline: 1px solid greenyellow; // height: 20px; + margin: 5px 10px 5px $avatar-column-space; + padding: 0; + font-size: 24px; + height: 30px; + &__link { + color: $status-secondary-color; + &:hover { + color: $status-links-color; + } + &:not(:last-child) { + margin-right: 15px; + } + } + + &__lock { + color: $status-secondary-color; + width: 24px; + + &:not(:last-child) { + margin-right: 15px; + } + } +} + +.boosted { + color: $boost-color; + + &:hover { + color: darken($boost-color, 10); + } +} + +.favorited { + color: $favorite-color; + + &:hover { + color: darken($favorite-color, 10); + } +} \ No newline at end of file diff --git a/src/app/components/stream/status/action-bar/action-bar.component.spec.ts b/src/app/components/stream/status/action-bar/action-bar.component.spec.ts new file mode 100644 index 00000000..be44980a --- /dev/null +++ b/src/app/components/stream/status/action-bar/action-bar.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ActionBarComponent } from './action-bar.component'; + +describe('ActionBarComponent', () => { + let component: ActionBarComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ActionBarComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ActionBarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/status/action-bar/action-bar.component.ts b/src/app/components/stream/status/action-bar/action-bar.component.ts new file mode 100644 index 00000000..84005dad --- /dev/null +++ b/src/app/components/stream/status/action-bar/action-bar.component.ts @@ -0,0 +1,191 @@ +import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'; +import { Store } from '@ngxs/store'; +import { Observable, Subscription } from 'rxjs'; + +import { StatusWrapper } from '../../stream.component'; +import { MastodonService } from '../../../../services/mastodon.service'; +import { AccountInfo } from '../../../../states/accounts.state'; +import { Status, Results } from '../../../../services/models/mastodon.interfaces'; +// import { map } from "rxjs/operators"; + +@Component({ + selector: 'app-action-bar', + templateUrl: './action-bar.component.html', + styleUrls: ['./action-bar.component.scss'] +}) +export class ActionBarComponent implements OnInit, OnDestroy { + + @Input() statusWrapper: StatusWrapper; + @Output() replyEvent = new EventEmitter(); + + isFavorited: boolean; + isBoosted: boolean; + + isBoostLocked: boolean; + isLocked: boolean; + + private isProviderSelected: boolean; + private selectedAccounts: AccountInfo[]; + + private favoriteStatePerAccountId: { [id: string]: boolean; } = {}; + private bootedStatePerAccountId: { [id: string]: boolean; } = {}; + + private accounts$: Observable; + private accountSub: Subscription; + + constructor( + private readonly store: Store, + private readonly mastodonService: MastodonService) { + + this.accounts$ = this.store.select(state => state.registeredaccounts.accounts); + } + + ngOnInit() { + // const selectedAccounts = this.getSelectedAccounts(); + // this.checkStatus(selectedAccounts); + + const status = this.statusWrapper.status; + const account = this.statusWrapper.provider; + this.favoriteStatePerAccountId[account.id] = status.favourited; + this.bootedStatePerAccountId[account.id] = status.reblogged; + + this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => { + this.checkStatus(accounts); + }); + } + + ngOnDestroy(): void { + this.accountSub.unsubscribe(); + } + + private checkStatus(accounts: AccountInfo[]): void { + const status = this.statusWrapper.status; + const provider = this.statusWrapper.provider; + this.selectedAccounts = accounts.filter(x => x.isSelected); + this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0; + + if (status.visibility === 'direct' || status.visibility === 'private') { + this.isBoostLocked = true; + } else { + this.isBoostLocked = false; + } + + if ((status.visibility === 'direct' || status.visibility === 'private') && !this.isProviderSelected) { + this.isLocked = true; + } else { + this.isLocked = false; + } + + this.checkIfFavorited(); + this.checkIfBoosted(); + } + + reply(): boolean { + this.replyEvent.emit(); + return false; + } + + boost(): boolean { + this.selectedAccounts.forEach((account: AccountInfo) => { + const isProvider = this.statusWrapper.provider.id === account.id; + + let pipeline: Promise = Promise.resolve(this.statusWrapper.status); + + if (!isProvider) { + pipeline = pipeline.then((foreignStatus: Status) => { + const statusUrl = foreignStatus.url; + return this.mastodonService.search(account, statusUrl) + .then((results: Results) => { + //TODO check and type errors + return results.statuses[0]; + }); + }); + } + + pipeline + .then((status: Status) => { + if (this.isBoosted) { + return this.mastodonService.unreblog(account, status); + } else { + return this.mastodonService.reblog(account, status); + } + }) + .then((boostedStatus: Status) => { + this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged; + this.checkIfBoosted(); + // this.isBoosted = !this.isBoosted; + }) + .catch(err => { + console.error(err); + }); + }); + + return false; + } + + favorite(): boolean { + this.selectedAccounts.forEach((account: AccountInfo) => { + const isProvider = this.statusWrapper.provider.id === account.id; + + let pipeline: Promise = Promise.resolve(this.statusWrapper.status); + + if (!isProvider) { + pipeline = pipeline.then((foreignStatus: Status) => { + const statusUrl = foreignStatus.url; + return this.mastodonService.search(account, statusUrl) + .then((results: Results) => { + //TODO check and type errors + return results.statuses[0]; + }); + }); + } + + pipeline + .then((status: Status) => { + if (this.isFavorited) { + return this.mastodonService.unfavorite(account, status); + } else { + return this.mastodonService.favorite(account, status); + } + }) + .then((favoritedStatus: Status) => { + this.favoriteStatePerAccountId[account.id] = favoritedStatus.favourited; + this.checkIfFavorited(); + // this.isFavorited = !this.isFavorited; + }) + .catch(err => { + console.error(err); + }); + }); + return false; + } + + private checkIfBoosted() { + const selectedAccount = this.selectedAccounts[0]; + if (selectedAccount) { + this.isBoosted = this.bootedStatePerAccountId[selectedAccount.id]; + } else { + this.isBoosted = false; + } + } + + private checkIfFavorited() { + const selectedAccount = this.selectedAccounts[0]; + + if (selectedAccount) { + this.isFavorited = this.favoriteStatePerAccountId[selectedAccount.id]; + } else { + this.isFavorited = false; + } + } + + more(): boolean { + console.warn('more'); + return false; + } + + private getSelectedAccounts(): AccountInfo[] { + var regAccounts = this.store.snapshot().registeredaccounts.accounts; + return regAccounts; + } +} diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.html b/src/app/components/stream/status/databinded-text/databinded-text.component.html new file mode 100644 index 00000000..96aa742f --- /dev/null +++ b/src/app/components/stream/status/databinded-text/databinded-text.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.scss b/src/app/components/stream/status/databinded-text/databinded-text.component.scss new file mode 100644 index 00000000..0df7c1c2 --- /dev/null +++ b/src/app/components/stream/status/databinded-text/databinded-text.component.scss @@ -0,0 +1,24 @@ +@import "variables"; + +.content { + cursor: pointer; +} + +//Mastodon styling +:host ::ng-deep .content { + // font-size: 14px; + color: $status-primary-color; + & a, + .mention, + .ellipsis { + color: $status-links-color; + } + & .invisible { + display: none; + } + & p { + margin: 0px; + //font-size: .9em; + // font-size: 14px; + } +} \ No newline at end of file diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts b/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts new file mode 100644 index 00000000..5df551dd --- /dev/null +++ b/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DatabindedTextComponent } from './databinded-text.component'; + +describe('DatabindedTextComponent', () => { + let component: DatabindedTextComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DatabindedTextComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DatabindedTextComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.ts b/src/app/components/stream/status/databinded-text/databinded-text.component.ts new file mode 100644 index 00000000..efe06769 --- /dev/null +++ b/src/app/components/stream/status/databinded-text/databinded-text.component.ts @@ -0,0 +1,125 @@ +import { Component, OnInit, Input, EventEmitter, Output, Renderer2, ViewChild, ElementRef } from '@angular/core'; +import { forEach } from '@angular/router/src/utils/collection'; + +@Component({ + selector: 'app-databinded-text', + templateUrl: './databinded-text.component.html', + styleUrls: ['./databinded-text.component.scss'] +}) +export class DatabindedTextComponent implements OnInit { + private accounts: string[] = []; + private hashtags: string[] = []; + // private links: string[] = []; + + processedText: string; + + @ViewChild('content') contentElement: ElementRef; + + @Output() accountSelected = new EventEmitter(); + @Output() hashtagSelected = new EventEmitter(); + @Output() textSelected = new EventEmitter(); + + @Input('text') + set text(value: string) { + this.processedText = ''; + + let linksSections = value.split(''); + let extractedHashtag = extractedLinkAndNext[0].split('#')[1].replace('', '').replace('', ''); + + this.processedText += ` #${extractedHashtag}`; + if (extractedLinkAndNext[1]) this.processedText += extractedLinkAndNext[1]; + this.hashtags.push(extractedHashtag); + + } else if (section.includes('class="u-url mention"')) { + let extractedAccountAndNext = section.split(''); + + let extractedAccountName = extractedAccountAndNext[0].split('@')[1].replace('', '').replace('', ''); + let extractedAccountLink = extractedAccountAndNext[0].split('" class="u-url mention"')[0].replace('href="https://', '').replace(' ', '').replace('@', '').split('/'); + let extractedAccount = `@${extractedAccountLink[1]}@${extractedAccountLink[0]}`; + + let classname = this.getClassName(extractedAccount); + this.processedText += ` @${extractedAccountName}`; + + if (extractedAccountAndNext[1]) this.processedText += extractedAccountAndNext[1]; + this.accounts.push(extractedAccount); + } else { + this.processedText += ` { + event.preventDefault(); + event.stopImmediatePropagation(); + + this.selectHashtag(hashtag); + return false; + }); + } + + for (const account of this.accounts) { + let classname = this.getClassName(account); + let el = this.contentElement.nativeElement.querySelector(`.${classname}`); + + this.renderer.listen(el, 'click', (event) => { + event.preventDefault(); + event.stopImmediatePropagation(); + + this.selectAccount(account); + return false; + }); + } + + // let allLinksEl = this.contentElement.nativeElement.querySelectorAll(`.link`); + // for (const link of allLinksEl) { + // this.renderer.listen(link, 'click', (event) => { + // //event.preventDefault(); + // event.stopImmediatePropagation(); + // return false; + // }); + // } + } + + private getClassName(value: string): string { + let res = value; + while(res.includes('.')) res = res.replace('.', '-'); + while(res.includes('@')) res = res.replace('@', '-'); + return res; + } + + private selectAccount(account: string) { + console.warn(`select ${account}`); + this.accountSelected.next(account); + } + + private selectHashtag(hashtag: string) { + console.warn(`select ${hashtag}`); + this.hashtagSelected.next(hashtag); + } + + selectText() { + console.warn(`selectText`); + this.textSelected.next(); + } + +} diff --git a/src/app/components/stream/status/reply-to-status/reply-to-status.component.html b/src/app/components/stream/status/reply-to-status/reply-to-status.component.html new file mode 100644 index 00000000..23d96921 --- /dev/null +++ b/src/app/components/stream/status/reply-to-status/reply-to-status.component.html @@ -0,0 +1,8 @@ +
+ + + + +
\ No newline at end of file diff --git a/src/app/components/stream/status/reply-to-status/reply-to-status.component.scss b/src/app/components/stream/status/reply-to-status/reply-to-status.component.scss new file mode 100644 index 00000000..b1abcad9 --- /dev/null +++ b/src/app/components/stream/status/reply-to-status/reply-to-status.component.scss @@ -0,0 +1,36 @@ +@import "variables"; +@import "panel"; +@import "buttons"; +$btn-send-status-width: 60px; +.form-control { + margin: 0 0 5px 5px; + width: calc(100% - 10px); + background-color: $column-color; + border-color: $status-secondary-color; + color: #fff; + font-size: $default-font-size; + + &:focus { + box-shadow: none; + } + + &--privacy { + display: inline-block; + width: calc(100% - 15px - #{$btn-send-status-width}); + } +} + +.btn-custom-primary { + display: inline-block; + width: $btn-send-status-width; + position: relative; + top: -1px; + left: 5px; // background-color: orange; + // border-color: orange; + // color: black; + font-weight: 500; // &:hover { + // } + // &:focus { + // border-color: darkblue; + // } +} \ No newline at end of file diff --git a/src/app/components/stream/status/reply-to-status/reply-to-status.component.spec.ts b/src/app/components/stream/status/reply-to-status/reply-to-status.component.spec.ts new file mode 100644 index 00000000..53e0c519 --- /dev/null +++ b/src/app/components/stream/status/reply-to-status/reply-to-status.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReplyToStatusComponent } from './reply-to-status.component'; + +describe('ReplyToStatusComponent', () => { + let component: ReplyToStatusComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReplyToStatusComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReplyToStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/status/reply-to-status/reply-to-status.component.ts b/src/app/components/stream/status/reply-to-status/reply-to-status.component.ts new file mode 100644 index 00000000..db9dbde0 --- /dev/null +++ b/src/app/components/stream/status/reply-to-status/reply-to-status.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit, Input, Output, EventEmitter, ElementRef, ViewChild } from '@angular/core'; +import { Store } from '@ngxs/store'; +import { MastodonService, VisibilityEnum } from '../../../../services/mastodon.service'; +import { AccountInfo } from '../../../../states/accounts.state'; +import { StatusWrapper } from '../../stream.component'; +import { Status } from '../../../../services/models/mastodon.interfaces'; + +@Component({ + selector: 'app-reply-to-status', + templateUrl: './reply-to-status.component.html', + styleUrls: ['./reply-to-status.component.scss'] +}) +export class ReplyToStatusComponent implements OnInit { + @Input() status: string = ''; + @Input() statusReplyingToWrapper: StatusWrapper; + @Output() onClose = new EventEmitter(); + @ViewChild('reply') replyElement: ElementRef; + + private statusReplyingTo: Status; + + selectedPrivacy = 'Public'; + privacyList: string[] = ['Public', 'Unlisted', 'Follows-only', 'DM']; + + constructor( + private readonly store: Store, + private readonly mastodonService: MastodonService) { } + + ngOnInit() { + this.statusReplyingTo = this.statusReplyingToWrapper.status; + + this.status += `@${this.statusReplyingTo.account.acct} `; + for (const mention of this.statusReplyingTo.mentions) { + this.status += `@${mention.acct} `; + } + + setTimeout(() => { + this.replyElement.nativeElement.focus(); + }, 0); + } + + onSubmit(): boolean { + const accounts = this.getRegisteredAccounts(); + const selectedAccounts = accounts.filter(x => x.isSelected); + + let visibility: VisibilityEnum = VisibilityEnum.Unknown; + switch (this.selectedPrivacy) { + case 'Public': + visibility = VisibilityEnum.Public; + break; + case 'Unlisted': + visibility = VisibilityEnum.Unlisted; + break; + case 'Follows-only': + visibility = VisibilityEnum.Private; + break; + case 'DM': + visibility = VisibilityEnum.Direct; + break; + } + + let spoiler = this.statusReplyingTo.spoiler_text; + + for (const acc of selectedAccounts) { + this.mastodonService.postNewStatus(acc, this.status, visibility, spoiler, this.statusReplyingTo.id) + .then((res: Status) => { + console.log(res); + this.status = ''; + this.onClose.emit(); + }); + } + + return false; + } + + private getRegisteredAccounts(): AccountInfo[] { + var regAccounts = this.store.snapshot().registeredaccounts.accounts; + return regAccounts; + } + + onCtrlEnter(): boolean { + this.onSubmit(); + return false; + } +} diff --git a/src/app/components/stream/status/status.component.html b/src/app/components/stream/status/status.component.html index 13612b9b..cdcd6638 100644 --- a/src/app/components/stream/status/status.component.html +++ b/src/app/components/stream/status/status.component.html @@ -1,9 +1,10 @@
- + @@ -13,23 +14,14 @@
{{ getCompactRelativeTime(status.created_at) }}
-
+ -
- -
+ -
- - - - -
- + + + + +
\ No newline at end of file diff --git a/src/app/components/stream/status/status.component.scss b/src/app/components/stream/status/status.component.scss index cd7dd691..6fd305fa 100644 --- a/src/app/components/stream/status/status.component.scss +++ b/src/app/components/stream/status/status.component.scss @@ -1,5 +1,5 @@ @import "variables"; -$avatar-column-space: 70px; + .reblog { position: relative; margin: 5px 0 0 10px; @@ -76,12 +76,13 @@ $avatar-column-space: 70px; &__content { /*width: calc(100% - 50px);*/ word-wrap: break-word; - margin: 0px 10px 10px $avatar-column-space; - } - &__content p { - margin: 0; - font-size: 0.85em; + margin: 0 10px 0 $avatar-column-space; + display: block; } + // &__content p { + // margin: 0 !important; + // font-size: 0.85em; + // } &__created-at { color: $status-secondary-color; position: absolute; @@ -90,35 +91,8 @@ $avatar-column-space: 70px; } } -//Mastodon styling -:host ::ng-deep .status__content { - color: $status-primary-color; - & a, - .mention, - .ellipsis { - color: $status-links-color; - } - & .invisible { - display: none; - } -} - .attachments { - width: calc(100% - 80px); - margin: 0px 10px 10px $avatar-column-space; + display: block; + // width: calc(100% - 80px); + margin: 10px 10px 0 $avatar-column-space; } - -.action-bar { - // outline: 1px solid greenyellow; // height: 20px; - margin: 0px 10px 10px $avatar-column-space; - font-size: 24px; - &__link { - color: $status-secondary-color; - &:hover { - color: $status-links-color; - } - &:not(:last-child) { - margin-right: 15px; - } - } -} \ No newline at end of file diff --git a/src/app/components/stream/status/status.component.ts b/src/app/components/stream/status/status.component.ts index b0c36d3d..17b0e2b3 100644 --- a/src/app/components/stream/status/status.component.ts +++ b/src/app/components/stream/status/status.component.ts @@ -1,8 +1,10 @@ -import { Component, OnInit, Input, Inject, LOCALE_ID } from "@angular/core"; -import { Status } from "../../../services/models/mastodon.interfaces"; +import { Component, OnInit, Input, Output, Inject, LOCALE_ID, ElementRef, EventEmitter, Pipe, PipeTransform, ViewChild, Renderer2 } from "@angular/core"; +import { Status, Account } from "../../../services/models/mastodon.interfaces"; import { formatDate } from '@angular/common'; import { stateNameErrorMessage } from "@ngxs/store/src/decorators/state"; +import { StatusWrapper } from "../stream.component"; +import { DomSanitizer } from '@angular/platform-browser' @Component({ selector: "app-status", @@ -13,37 +15,67 @@ export class StatusComponent implements OnInit { displayedStatus: Status; reblog: boolean; hasAttachments: boolean; + replyingToStatus: boolean; - private _status: Status; - @Input('status') - set status(value: Status) { - this._status = value; + @Output() browseAccount = new EventEmitter(); + @Output() browseHashtag = new EventEmitter(); + @Output() browseThread = new EventEmitter(); - if(this.status.reblog){ + + + private _statusWrapper: StatusWrapper; + status: Status; + @Input('statusWrapper') + set statusWrapper(value: StatusWrapper) { + this._statusWrapper = value; + this.status = value.status; + + //TEST + //this.status.content += '

TEST'; + + + if (this.status.reblog) { this.reblog = true; - this.displayedStatus = this._status.reblog; + this.displayedStatus = this.status.reblog; } else { - this.displayedStatus = this._status; + this.displayedStatus = this.status; } - - if(!this.displayedStatus.account.display_name){ + + if (!this.displayedStatus.account.display_name) { this.displayedStatus.account.display_name = this.displayedStatus.account.username; } - if(this.displayedStatus.media_attachments && this.displayedStatus.media_attachments.length > 0){ + if (this.displayedStatus.media_attachments && this.displayedStatus.media_attachments.length > 0) { this.hasAttachments = true; } - - } - get status(): Status{ - return this._status; + } - + get statusWrapper(): StatusWrapper { + return this._statusWrapper; + } + constructor(@Inject(LOCALE_ID) private locale: string) { } - ngOnInit() { + ngOnInit() { + } + + // ngAfterViewInit() { + // let el = this.contentElement.nativeElement.querySelector('.test'); + // console.log(this.contentElement.nativeElement); + // console.log(el); + // if (el) + // this.renderer.listen(el, 'click', (el2) => { + // console.log(el2); + // console.warn('YOOOOO'); + // return false; + // }); + // } + + openAccount(account: Account): boolean { + this.browseAccount.next(account); + return false; } getCompactRelativeTime(d: string): string { @@ -56,11 +88,27 @@ export class StatusComponent implements OnInit { } else if (timeDelta < 60 * 60) { return `${timeDelta / 60 | 0}m`; } else if (timeDelta < 60 * 60 * 24) { - return `${timeDelta / (60 * 60)| 0}h`; + return `${timeDelta / (60 * 60) | 0}h`; } else if (timeDelta < 60 * 60 * 24 * 31) { return `${timeDelta / (60 * 60 * 24) | 0}d`; } - + return formatDate(date, 'MM/dd', this.locale); } + + openReply(): boolean { + this.replyingToStatus = !this.replyingToStatus; + + return false; + } + + closeReply(): boolean { + this.replyingToStatus = false; + return false; + } + + test(): boolean { + console.warn('heeeeyaaa!'); + return false; + } } diff --git a/src/app/components/stream/stream-overlay/stream-overlay.component.html b/src/app/components/stream/stream-overlay/stream-overlay.component.html new file mode 100644 index 00000000..3939df1c --- /dev/null +++ b/src/app/components/stream/stream-overlay/stream-overlay.component.html @@ -0,0 +1,13 @@ +
+
+ CLOSE + PREV + NEXT +
+ + + + +
\ No newline at end of file diff --git a/src/app/components/stream/stream-overlay/stream-overlay.component.scss b/src/app/components/stream/stream-overlay/stream-overlay.component.scss new file mode 100644 index 00000000..2a2b18e7 --- /dev/null +++ b/src/app/components/stream/stream-overlay/stream-overlay.component.scss @@ -0,0 +1,51 @@ +@import "variables"; +.stream-overlay { + // position: absolute; + + // z-index: 50; + width: $stream-column-width; + height: calc(100%); + background-color: $column-color; + + + // margin: 0 0 0 $stream-column-separator; + // outline: 1px red solid; + // float: left; + &__header { + width: calc(100%); + height: 30px; + background-color: $column-header-background-color; + padding: 6px 10px 0 10px; + & a { + color: whitesmoke; + font-size: 0.8em; + font-weight: normal; + margin: 0; + } + } + + &__title { + width: calc(100%); + height: 30px; + background-color: $column-header-background-color; + border-top: 1px solid whitesmoke; + border-bottom: 1px solid whitesmoke; + padding: 3px 10px 0 10px; + } +} + +.overlay-previous { + display: block; + float: left; +} + +.overlay-next { + display: block; + float: right; + padding-right: 20px; +} + +.overlay-close { + display: block; + float: right; +} \ No newline at end of file diff --git a/src/app/components/stream/stream-overlay/stream-overlay.component.spec.ts b/src/app/components/stream/stream-overlay/stream-overlay.component.spec.ts new file mode 100644 index 00000000..4df07367 --- /dev/null +++ b/src/app/components/stream/stream-overlay/stream-overlay.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StreamOverlayComponent } from './stream-overlay.component'; + +describe('StreamOverlayComponent', () => { + let component: StreamOverlayComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ StreamOverlayComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StreamOverlayComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/stream-overlay/stream-overlay.component.ts b/src/app/components/stream/stream-overlay/stream-overlay.component.ts new file mode 100644 index 00000000..2483427f --- /dev/null +++ b/src/app/components/stream/stream-overlay/stream-overlay.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; +import { Account } from "../../../services/models/mastodon.interfaces"; + +@Component({ + selector: 'app-stream-overlay', + templateUrl: './stream-overlay.component.html', + styleUrls: ['./stream-overlay.component.scss'] +}) +export class StreamOverlayComponent implements OnInit { + private account: Account; + private thread: string; + private hashtag: string; + + @Output() closeOverlay = new EventEmitter(); + + @Input('browseAccount') + set browseAccount(account: Account) { + this.account = account; + } + get browseAccount(): Account{ + return this.account; + } + + @Input('browseThread') + set browseThread(thread: string) { + this.thread = thread; + } + get browseThread(): string{ + return this.thread; + } + + @Input('browseHashtag') + set browseHashtag(hashtag: string) { + this.hashtag = hashtag; + } + get browseHashtag(): string{ + return this.hashtag; + } + + constructor() { } + + ngOnInit() { + } + + close(): boolean { + this.closeOverlay.next(); + return false; + } + +} diff --git a/src/app/components/stream/stream.component.html b/src/app/components/stream/stream.component.html index fbfd0357..f08439cc 100644 --- a/src/app/components/stream/stream.component.html +++ b/src/app/components/stream/stream.component.html @@ -1,11 +1,17 @@
- - - \ No newline at end of file +
+ +
+ +
+
+
\ No newline at end of file diff --git a/src/app/components/stream/stream.component.scss b/src/app/components/stream/stream.component.scss index 13fd9e62..a2b72d6e 100644 --- a/src/app/components/stream/stream.component.scss +++ b/src/app/components/stream/stream.component.scss @@ -1,13 +1,15 @@ @import "variables"; .stream-column { + position: relative; + width: $stream-column-width; height: calc(100%); - background-color: #0f111a; + background-color: $column-color; margin: 0 0 0 $stream-column-separator; &__stream-header { width: calc(100%); height: 30px; - background-color: black; + background-color: $column-header-background-color; border-bottom: 1px solid black; & h1 { color: whitesmoke; @@ -47,4 +49,56 @@ background: lighten($color-primary, 5); // -webkit-box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.5); } -} \ No newline at end of file +} + +.stream-overlay { + position: absolute; + + z-index: 50; + width: $stream-column-width; + height: calc(100%); + + // background-color: rgba(#ff0000, 0.3); + // // margin: 0 0 0 $stream-column-separator; + // // outline: 1px red solid; + // // float: left; + // &__header { + // width: calc(100%); + // height: 30px; + // background-color: $column-header-background-color; + // padding: 6px 10px 0 10px; + // & a { + // color: whitesmoke; + // font-size: 0.8em; + // font-weight: normal; + // margin: 0; + // } + // } + + // &__title { + // width: calc(100%); + // height: 30px; + // background-color: $column-header-background-color; + // border-top: 1px solid whitesmoke; + // border-bottom: 1px solid whitesmoke; + // padding: 3px 10px 0 10px; + // } +} + +// .overlay-previous { +// display: block; +// float: left; +// } + + +// .overlay-next { +// display: block; +// float: right; +// padding-right: 20px; +// } + + +// .overlay-close { +// display: block; +// float: right; +// } \ No newline at end of file diff --git a/src/app/components/stream/stream.component.ts b/src/app/components/stream/stream.component.ts index 7cb6b257..82c62829 100644 --- a/src/app/components/stream/stream.component.ts +++ b/src/app/components/stream/stream.component.ts @@ -17,10 +17,13 @@ export class StreamComponent implements OnInit { private account: AccountInfo; private websocketStreaming: StreamingWrapper; - statuses: Status[] = []; + statuses: StatusWrapper[] = []; private bufferStream: Status[] = []; private bufferWasCleared: boolean; + overlayActive: boolean; + overlayAccountToBrowse: Account; + @Input() set streamElement(streamElement: StreamElement) { this._streamElement = streamElement; @@ -47,6 +50,26 @@ export class StreamComponent implements OnInit { ngOnInit() { } + browseAccount(account: Account): void { + this.overlayAccountToBrowse = account; + this.overlayActive = true; + } + + browseHashtag(hashtag: any): void { + console.warn('browseHashtag'); + console.warn(hashtag); + } + + browseThread(thread: any): void { + console.warn('browseThread'); + console.warn(thread); + } + + closeOverlay(): void { + this.overlayAccountToBrowse = null; + this.overlayActive = false; + } + @ViewChild('statusstream') public statustream: ElementRef; goToTop(): boolean { this.loadBuffer(); @@ -90,7 +113,8 @@ export class StreamComponent implements OnInit { } for (const status of this.bufferStream) { - this.statuses.unshift(status); + const wrapper = new StatusWrapper(status, this.account); + this.statuses.unshift(wrapper); } this.bufferStream.length = 0; @@ -100,10 +124,11 @@ export class StreamComponent implements OnInit { this.isProcessingInfiniteScroll = true; const lastStatus = this.statuses[this.statuses.length - 1]; - this.mastodonService.getTimeline(this.account, this._streamElement.type, lastStatus.id) + this.mastodonService.getTimeline(this.account, this._streamElement.type, lastStatus.status.id) .then((status: Status[]) => { for (const s of status) { - this.statuses.push(s); + const wrapper = new StatusWrapper(s, this.account); + this.statuses.push(wrapper); } }) .catch(err => { @@ -123,7 +148,8 @@ export class StreamComponent implements OnInit { this.mastodonService.getTimeline(this.account, this._streamElement.type) .then((results: Status[]) => { for (const s of results) { - this.statuses.push(s); + const wrapper = new StatusWrapper(s, this.account); + this.statuses.push(wrapper); } }); } @@ -133,9 +159,10 @@ export class StreamComponent implements OnInit { this.websocketStreaming.statusUpdateSubjet.subscribe((update: StatusUpdate) => { if (update) { if (update.type === EventEnum.update) { - if (!this.statuses.find(x => x.id == update.status.id)) { + if (!this.statuses.find(x => x.status.id == update.status.id)) { if (this.streamPositionnedAtTop) { - this.statuses.unshift(update.status); + const wrapper = new StatusWrapper(update.status, this.account); + this.statuses.unshift(wrapper); } else { this.bufferStream.push(update.status); } @@ -146,7 +173,6 @@ export class StreamComponent implements OnInit { this.checkAndCleanUpStream(); }); } - private checkAndCleanUpStream(): void { if (this.streamPositionnedAtTop && this.statuses.length > 60) { @@ -159,4 +185,11 @@ export class StreamComponent implements OnInit { } } +} + +export class StatusWrapper { + constructor( + public status: Status, + public provider: AccountInfo + ) {} } \ No newline at end of file diff --git a/src/app/components/stream/thread/thread.component.html b/src/app/components/stream/thread/thread.component.html new file mode 100644 index 00000000..5d42ddbd --- /dev/null +++ b/src/app/components/stream/thread/thread.component.html @@ -0,0 +1,3 @@ +

+ thread works! +

diff --git a/src/app/components/stream/thread/thread.component.scss b/src/app/components/stream/thread/thread.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/stream/thread/thread.component.spec.ts b/src/app/components/stream/thread/thread.component.spec.ts new file mode 100644 index 00000000..7c883807 --- /dev/null +++ b/src/app/components/stream/thread/thread.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ThreadComponent } from './thread.component'; + +describe('ThreadComponent', () => { + let component: ThreadComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ThreadComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ThreadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/thread/thread.component.ts b/src/app/components/stream/thread/thread.component.ts new file mode 100644 index 00000000..d72da5ec --- /dev/null +++ b/src/app/components/stream/thread/thread.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-thread', + templateUrl: './thread.component.html', + styleUrls: ['./thread.component.scss'] +}) +export class ThreadComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/components/stream/user-profile/user-profile.component.html b/src/app/components/stream/user-profile/user-profile.component.html new file mode 100644 index 00000000..20c9f289 --- /dev/null +++ b/src/app/components/stream/user-profile/user-profile.component.html @@ -0,0 +1,9 @@ +
+ header + header +

{{account.display_name}}

+

@{{account.acct}}

+
+
+

+
\ No newline at end of file diff --git a/src/app/components/stream/user-profile/user-profile.component.scss b/src/app/components/stream/user-profile/user-profile.component.scss new file mode 100644 index 00000000..c3d4b91a --- /dev/null +++ b/src/app/components/stream/user-profile/user-profile.component.scss @@ -0,0 +1,48 @@ +@import "variables"; +.profile-header { + position: relative; + height: 140px; + overflow: hidden; // background-color: black; + border-bottom: 1px solid black; + & h2 { + font-size: $default-font-size; + } + &__header { + position: absolute; + // width: calc(100%); + + width: calc(100%); + height: auto; + + float: left; + display: block; + opacity: 0.3; + } + &__avatar { + position: absolute; + top: 15px; + left: 15px; + width: 80px; + border-radius: 50%; // border: 1px solid black; + // background-color: black; + } + &__display-name { + position: absolute; + top: 45px; + left: 115px; + // font-weight: bold; + color: white; + } + &__fullhandle { + position: absolute; + top: 105px; + left: 15px; + color: white; + } +} + +.profile-description { + padding: 5px 10px 0 10px; + font-size: 13px; + border-bottom: 1px solid black; +} \ No newline at end of file diff --git a/src/app/components/stream/user-profile/user-profile.component.spec.ts b/src/app/components/stream/user-profile/user-profile.component.spec.ts new file mode 100644 index 00000000..e683b969 --- /dev/null +++ b/src/app/components/stream/user-profile/user-profile.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserProfileComponent } from './user-profile.component'; + +describe('UserProfileComponent', () => { + let component: UserProfileComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UserProfileComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/stream/user-profile/user-profile.component.ts b/src/app/components/stream/user-profile/user-profile.component.ts new file mode 100644 index 00000000..0c0bf024 --- /dev/null +++ b/src/app/components/stream/user-profile/user-profile.component.ts @@ -0,0 +1,26 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Account } from "../../../services/models/mastodon.interfaces"; + +@Component({ + selector: 'app-user-profile', + templateUrl: './user-profile.component.html', + styleUrls: ['./user-profile.component.scss'] +}) +export class UserProfileComponent implements OnInit { + account: Account; + hasNote: boolean; + + @Input('currentAccount') + set currentAccount(account: Account) { + this.account = account; + this.hasNote = account && account.note && account.note !== '

'; + console.warn('currentAccount'); + console.warn(account); + } + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/components/waiting-animation/waiting-animation.component.html b/src/app/components/waiting-animation/waiting-animation.component.html new file mode 100644 index 00000000..b550ca30 --- /dev/null +++ b/src/app/components/waiting-animation/waiting-animation.component.html @@ -0,0 +1,7 @@ + +
+
+
+
+
+
\ No newline at end of file diff --git a/src/app/components/waiting-animation/waiting-animation.component.scss b/src/app/components/waiting-animation/waiting-animation.component.scss new file mode 100644 index 00000000..6c6ae0d9 --- /dev/null +++ b/src/app/components/waiting-animation/waiting-animation.component.scss @@ -0,0 +1,67 @@ +//https://loading.io/css/ +.lds-ellipsis { + display: inline-block; + position: relative; + //width: 64px; + //height: 64px; + width: 40px; + height: 20px; + } + .lds-ellipsis div { + position: absolute; + // top: 27px; + // width: 11px; + // height: 11px; + + // top: 27px; + top: 8px; + width: 5px; + height: 5px; + + border-radius: 50%; + background: #fff; + animation-timing-function: cubic-bezier(0, 1, 1, 0); + } + .lds-ellipsis div:nth-child(1) { + left: 6px; + animation: lds-ellipsis1 0.6s infinite; + } + .lds-ellipsis div:nth-child(2) { + left: 6px; + animation: lds-ellipsis2 0.6s infinite; + } + .lds-ellipsis div:nth-child(3) { + left: 16px; + // left: 26px; + animation: lds-ellipsis2 0.6s infinite; + } + .lds-ellipsis div:nth-child(4) { + left: 25px; + // left: 45px; + animation: lds-ellipsis3 0.6s infinite; + } + @keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } + } + @keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(10px, 0); + } + } + @keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } + } + \ No newline at end of file diff --git a/src/app/components/waiting-animation/waiting-animation.component.spec.ts b/src/app/components/waiting-animation/waiting-animation.component.spec.ts new file mode 100644 index 00000000..e96e388b --- /dev/null +++ b/src/app/components/waiting-animation/waiting-animation.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WaitingAnimationComponent } from './waiting-animation.component'; + +describe('WaitingAnimationComponent', () => { + let component: WaitingAnimationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ WaitingAnimationComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WaitingAnimationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/waiting-animation/waiting-animation.component.ts b/src/app/components/waiting-animation/waiting-animation.component.ts new file mode 100644 index 00000000..bf8d762c --- /dev/null +++ b/src/app/components/waiting-animation/waiting-animation.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-waiting-animation', + templateUrl: './waiting-animation.component.html', + styleUrls: ['./waiting-animation.component.scss'] +}) +export class WaitingAnimationComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/services/mastodon.service.ts b/src/app/services/mastodon.service.ts index a189e679..39c82a3f 100644 --- a/src/app/services/mastodon.service.ts +++ b/src/app/services/mastodon.service.ts @@ -5,10 +5,11 @@ import { ApiRoutes } from './models/api.settings'; import { Account, Status, Results } from "./models/mastodon.interfaces"; import { AccountInfo } from '../states/accounts.state'; import { StreamTypeEnum } from '../states/streams.state'; +import { stat } from 'fs'; @Injectable() export class MastodonService { - + private apiRoutes = new ApiRoutes(); constructor(private readonly httpClient: HttpClient) { } @@ -115,6 +116,30 @@ export class MastodonService { const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.get(route, { headers: headers }).toPromise() } + + reblog(account: AccountInfo, status: Status): Promise { + const route = `https://${account.instance}${this.apiRoutes.reblogStatus}`.replace('{0}', status.id); + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.post(route, null, { headers: headers }).toPromise() + } + + unreblog(account: AccountInfo, status: Status): Promise { + const route = `https://${account.instance}${this.apiRoutes.unreblogStatus}`.replace('{0}', status.id); + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.post(route, null, { headers: headers }).toPromise() + } + + favorite(account: AccountInfo, status: Status): any { + const route = `https://${account.instance}${this.apiRoutes.favouritingStatus}`.replace('{0}', status.id); + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.post(route, null, { headers: headers }).toPromise() + } + + unfavorite(account: AccountInfo, status: Status): any { + const route = `https://${account.instance}${this.apiRoutes.unfavouritingStatus}`.replace('{0}', status.id); + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.post(route, null, { headers: headers }).toPromise() + } } export enum VisibilityEnum { diff --git a/src/app/services/models/mastodon.interfaces.ts b/src/app/services/models/mastodon.interfaces.ts index 2d4aeb0a..c07620cc 100644 --- a/src/app/services/models/mastodon.interfaces.ts +++ b/src/app/services/models/mastodon.interfaces.ts @@ -116,15 +116,18 @@ export interface Status { created_at: string; reblogs_count: string; favourites_count: string; - reblogged: string; - favourited: string; - sensitive: string; + reblogged: boolean; + favourited: boolean; + sensitive: boolean; spoiler_text: string; visibility: string; media_attachments: Attachment[]; - mentions: string; - tags: string; + mentions: Mention[]; + tags: Tag[]; application: Application; + emojis: any[]; + language: string; + pinned: boolean; } export interface Tag { name: string; diff --git a/src/index.html b/src/index.html index ff94bff9..1d470ce9 100644 --- a/src/index.html +++ b/src/index.html @@ -2,24 +2,65 @@ - - Sengi - + + Sengi + - - - + + + - + - - loading... - + +
+
+
+
+
- + \ No newline at end of file diff --git a/src/sass/_commons.scss b/src/sass/_commons.scss new file mode 100644 index 00000000..4bd0c225 --- /dev/null +++ b/src/sass/_commons.scss @@ -0,0 +1,5 @@ +.waiting-icon { + width: 40px; + display: block; + margin: 5px auto; +} \ No newline at end of file diff --git a/src/sass/_panel.scss b/src/sass/_panel.scss index aefae37a..4178e73f 100644 --- a/src/sass/_panel.scss +++ b/src/sass/_panel.scss @@ -1,4 +1,5 @@ .panel{ + width: 100%; padding: 10px 10px 0 7px; font-size: $small-font-size; &__title { @@ -6,4 +7,4 @@ text-transform: uppercase; margin: 6px 0 12px 0; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/sass/_variables.scss b/src/sass/_variables.scss index ae8164e5..f8cb7a6f 100644 --- a/src/sass/_variables.scss +++ b/src/sass/_variables.scss @@ -4,28 +4,33 @@ $font-link-primary: #595c67; $font-link-primary-hover: #8f93a2; $color-primary: #141824; $color-secondary: #090b10; + +$column-color: #0f111a; +$column-header-background-color: black; + + + $default-font-size: 15px; $small-font-size: 12px; - -$btn-primary-color: #515a62; -$btn-primary-color: #254d6f; +// $btn-primary-color: #515a62; +// $btn-primary-color: #254d6f; $btn-primary-color: #444f74; $btn-primary-color-hover: darken($btn-primary-color, 10); $btn-primary-font-color: white; - // TEST 1 $status-primary-color: #fff; -$status-secondary-color: #353e64; +// $status-secondary-color: #353e64; $status-secondary-color: #4e5572; $status-links-color: #d9e1e8; // $status-primary-color : #8f93a2; // $status-primary-color : lighten(#8f93a2, 30); // $status-links-color : #b2ccd6; - +$boost-color : #5098eb; +$favorite-color: #ffc16f; // Block dispositions $stream-selector-height: 30px; $stream-column-separator: 7px; $stream-column-width: 320px; - +$avatar-column-space: 70px; //Bootstrap cuistomization -$enable-rounded : false; \ No newline at end of file +$enable-rounded: false; \ No newline at end of file