Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 931ac91526 | |||
| c56b0aab8a |
@@ -42,12 +42,12 @@
|
|||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
"maximumWarning": "500kB",
|
"maximumWarning": "500kB",
|
||||||
"maximumError": "1MB"
|
"maximumError": "100MB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
"maximumWarning": "4kB",
|
"maximumWarning": "4kB",
|
||||||
"maximumError": "8kB"
|
"maximumError": "100MB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputHashing": "all",
|
"outputHashing": "all",
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -16,7 +16,7 @@
|
|||||||
"@angular/platform-browser": "^21.2.0",
|
"@angular/platform-browser": "^21.2.0",
|
||||||
"@angular/router": "^21.2.0",
|
"@angular/router": "^21.2.0",
|
||||||
"@angular/service-worker": "^21.2.0",
|
"@angular/service-worker": "^21.2.0",
|
||||||
"@chatenium/chatenium-sdk": "^1.1.7",
|
"@chatenium/chatenium-sdk": "^1.1.8",
|
||||||
"@fortawesome/angular-fontawesome": "^4.0.0",
|
"@fortawesome/angular-fontawesome": "^4.0.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||||
@@ -1011,9 +1011,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@chatenium/chatenium-sdk": {
|
"node_modules/@chatenium/chatenium-sdk": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@chatenium/chatenium-sdk/-/chatenium-sdk-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@chatenium/chatenium-sdk/-/chatenium-sdk-1.1.8.tgz",
|
||||||
"integrity": "sha512-8stVVjKwwWZvUfVeWzSK1WGIiSt95Yxr80nfltZSDmqMKQaEA1sL9/2V6pobM4mCs9nzjUQQiniEPVQ0uxPXAg==",
|
"integrity": "sha512-UuPCr/NqZhLrT/lt8I1AM+UNFAdI00adtvvlu3BQgqCYPASoUpTJJ4HdMzJ+E0cb0pUS6DmN0Kv+w3vFQfRYwg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@faker-js/faker": "^10.4.0",
|
"@faker-js/faker": "^10.4.0",
|
||||||
"axios": "^1.14.0",
|
"axios": "^1.14.0",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"@angular/platform-browser": "^21.2.0",
|
"@angular/platform-browser": "^21.2.0",
|
||||||
"@angular/router": "^21.2.0",
|
"@angular/router": "^21.2.0",
|
||||||
"@angular/service-worker": "^21.2.0",
|
"@angular/service-worker": "^21.2.0",
|
||||||
"@chatenium/chatenium-sdk": "^1.1.7",
|
"@chatenium/chatenium-sdk": "^1.1.8",
|
||||||
"@fortawesome/angular-fontawesome": "^4.0.0",
|
"@fortawesome/angular-fontawesome": "^4.0.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
"@fortawesome/free-brands-svg-icons": "^7.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "3.0 Beta 4 (April 11, 2026)",
|
"version": "3.0 Beta 5 (April 13, 2026)",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"aChatProgram": "A messaging platform that you can trust.",
|
"aChatProgram": "A messaging platform that you can trust.",
|
||||||
@@ -104,11 +104,15 @@
|
|||||||
"changeLogDialog": {
|
"changeLogDialog": {
|
||||||
"label": "Chatenium has been updated",
|
"label": "Chatenium has been updated",
|
||||||
"changeLog": {
|
"changeLog": {
|
||||||
"1": "Added support for text channels inside networks",
|
"1": "Started implementing Chatenium Pictures",
|
||||||
"2": "Bug fixes in message box"
|
"2": "Made small changes to the UI",
|
||||||
|
"3": "Minor bug fixes in message box"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chatnav": {
|
"chatnav": {
|
||||||
|
"pictureList": {
|
||||||
|
"myPictures": "My pictures"
|
||||||
|
},
|
||||||
"dmList": {
|
"dmList": {
|
||||||
"newChat": "Start new chat",
|
"newChat": "Start new chat",
|
||||||
"messageBox": {
|
"messageBox": {
|
||||||
@@ -121,6 +125,11 @@
|
|||||||
"newNetwork": "Start new network"
|
"newNetwork": "Start new network"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pictures": {
|
||||||
|
"albumList": {
|
||||||
|
"noAlbum": "This user has no albums published"
|
||||||
|
}
|
||||||
|
},
|
||||||
"elements": {
|
"elements": {
|
||||||
"messageBox": {
|
"messageBox": {
|
||||||
"editMessageLabel": "Editing message: ",
|
"editMessageLabel": "Editing message: ",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {Privacy} from './privacy/privacy';
|
|||||||
import {TOS} from './tos/tos';
|
import {TOS} from './tos/tos';
|
||||||
import {Network} from './chat/network/network';
|
import {Network} from './chat/network/network';
|
||||||
import {Text} from './chat/network/channel/text/text';
|
import {Text} from './chat/network/channel/text/text';
|
||||||
|
import {See} from './chat/picture/see/see';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{path: '', component: Homepage},
|
{path: '', component: Homepage},
|
||||||
@@ -18,6 +19,7 @@ export const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'chat', component: Chat, canActivate: [authNeededGuard], children: [
|
path: 'chat', component: Chat, canActivate: [authNeededGuard], children: [
|
||||||
{path: 'dm/:chatid', component: Dm},
|
{path: 'dm/:chatid', component: Dm},
|
||||||
|
{path: 'picture/:uploaderId', component: See},
|
||||||
{
|
{
|
||||||
path: 'network/:networkId', component: Network, children: [
|
path: 'network/:networkId', component: Network, children: [
|
||||||
{path: ":categoryId/:channelId", component: Text}
|
{path: ":categoryId/:channelId", component: Text}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<button>
|
<button>
|
||||||
<tui-icon icon="@tui.network"/>
|
<tui-icon icon="@tui.network"/>
|
||||||
</button>
|
</button>
|
||||||
<button disabled style="pointer-events: none; opacity: 0.5">
|
<button>
|
||||||
<tui-icon icon="@tui.image"/>
|
<tui-icon icon="@tui.image"/>
|
||||||
</button>
|
</button>
|
||||||
</tui-segmented>
|
</tui-segmented>
|
||||||
@@ -47,13 +47,17 @@
|
|||||||
<network-list [token]="serviceManager.currentSession()!.token"
|
<network-list [token]="serviceManager.currentSession()!.token"
|
||||||
[userid]="serviceManager.currentSession()!.userData.userid"></network-list>
|
[userid]="serviceManager.currentSession()!.userData.userid"></network-list>
|
||||||
}
|
}
|
||||||
|
@case (2) {
|
||||||
|
<app-picture-list [token]="serviceManager.currentSession()!.token"
|
||||||
|
[userid]="serviceManager.currentSession()!.userData.userid"></app-picture-list>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</main>
|
</main>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main id="content">
|
<main id="content">
|
||||||
<div id="content_tint">
|
<div id="content_tint">
|
||||||
@if (router.url.startsWith("/chat/dm")) {
|
@if (router.url.startsWith("/chat/dm") && router.url.startsWith("/chat/picture")) {
|
||||||
<!-- To ensure data is loaded -->
|
<!-- To ensure data is loaded -->
|
||||||
@defer (when serviceManager.chatsStatus() != LoadStatus.loading) {
|
@defer (when serviceManager.chatsStatus() != LoadStatus.loading) {
|
||||||
<router-outlet/>
|
<router-outlet/>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {TranslatePipe, TranslateService} from '@ngx-translate/core';
|
|||||||
import {environment} from '../../environments/environment';
|
import {environment} from '../../environments/environment';
|
||||||
import {TuiTabBarComponent, TuiTabBarItem} from '@taiga-ui/addon-mobile';
|
import {TuiTabBarComponent, TuiTabBarItem} from '@taiga-ui/addon-mobile';
|
||||||
import {NetworkList} from './network-list/network-list';
|
import {NetworkList} from './network-list/network-list';
|
||||||
|
import {PictureList} from './picture-list/picture-list';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-chat',
|
selector: 'app-chat',
|
||||||
@@ -29,7 +30,8 @@ import {NetworkList} from './network-list/network-list';
|
|||||||
TranslatePipe,
|
TranslatePipe,
|
||||||
TuiTabBarComponent,
|
TuiTabBarComponent,
|
||||||
TuiTabBarItem,
|
TuiTabBarItem,
|
||||||
NetworkList
|
NetworkList,
|
||||||
|
PictureList
|
||||||
],
|
],
|
||||||
templateUrl: './chat.html',
|
templateUrl: './chat.html',
|
||||||
styleUrl: './chat.scss',
|
styleUrl: './chat.scss',
|
||||||
@@ -72,6 +74,8 @@ export class Chat implements OnInit {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.router.url.startsWith("/chat/network")) {
|
if (this.router.url.startsWith("/chat/network")) {
|
||||||
this.navigationActiveIndex = 1
|
this.navigationActiveIndex = 1
|
||||||
|
} else if (this.router.url.startsWith("/chat/picture")) {
|
||||||
|
this.navigationActiveIndex = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
this.indexedDb.openDatabase().then(async () => {
|
this.indexedDb.openDatabase().then(async () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
|
|
||||||
import { DmList } from './dm-list';
|
import { DmList } from './dm-list';
|
||||||
|
|
||||||
describe('DmList', () => {
|
describe('PictureList', () => {
|
||||||
let component: DmList;
|
let component: DmList;
|
||||||
let fixture: ComponentFixture<DmList>;
|
let fixture: ComponentFixture<DmList>;
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ export class Dm implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.messageBox.editingMessage.set(null)
|
|
||||||
this.store.messageBox.message.set("")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
:host {
|
:host {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 4px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
img {
|
::ng-deep img {
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ export class MessageBox {
|
|||||||
this.viewModel().message.set("")
|
this.viewModel().message.set("")
|
||||||
this.viewModel().files.set([])
|
this.viewModel().files.set([])
|
||||||
this.viewModel().dialogOpen.set(false)
|
this.viewModel().dialogOpen.set(false)
|
||||||
|
this.viewModel().editingMessage.set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEnterKeydown(e: any) {
|
handleEnterKeydown(e: any) {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
@for (network of serviceManager.networks(); track network.networkId) {
|
@for (network of serviceManager.networks(); track network.networkId) {
|
||||||
<button [class.enlarge]="breakpoint() == 'mobile'" tuiButton
|
<button [class.enlarge]="breakpoint() == 'mobile'" tuiButton
|
||||||
[appearance]="router.url == '/chat/network/' + network.networkId ? 'primary' : 'flat'"
|
[appearance]="router.url.startsWith('/chat/network/' + network.networkId) ? 'primary' : 'flat'"
|
||||||
[routerLink]="'/chat/network/' + network.networkId">
|
[routerLink]="'/chat/network/' + network.networkId">
|
||||||
<oimg [src]="network.picture" height="35px" width="35px" [radius]="10"></oimg>
|
<oimg [src]="network.picture" height="35px" width="35px" [radius]="10"></oimg>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
|
|||||||
@@ -120,8 +120,6 @@ export class Text {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.messageBox.message.set("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteMessage(messageId: string) {
|
async deleteMessage(messageId: string) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<div tuiGroup orientation="vertical" style="width: 100%">
|
<div tuiGroup orientation="vertical" style="width: 100%">
|
||||||
@for (channel of category.channels; track channel) {
|
@for (channel of category.channels; track channel) {
|
||||||
<button tuiButton class="channel" appearance="secondary" [disabled]="channel.type != 'message'" [routerLink]="'/chat/network/'+store.networkData().networkId+'/'+category.categoryId+'/'+channel.channelId">
|
<button tuiButton class="channel" [appearance]="router.url.endsWith(channel.channelId) ? 'primary' : 'secondary'" [disabled]="channel.type != 'message'" [routerLink]="'/chat/network/'+store.networkData().networkId+'/'+category.categoryId+'/'+channel.channelId">
|
||||||
@switch (channel.type) {
|
@switch (channel.type) {
|
||||||
@case ("message") {
|
@case ("message") {
|
||||||
<tui-icon icon="@tui.hash"></tui-icon>
|
<tui-icon icon="@tui.hash"></tui-icon>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
grid-template-columns: 1fr 300px;
|
grid-template-columns: 1fr 300px;
|
||||||
|
|
||||||
#router {
|
#router {
|
||||||
background: var(--tui-background-neutral-1-pressed);
|
background: var(--tui-background-neutral-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, inject, OnInit, signal} from '@angular/core';
|
import {Component, inject, OnInit, signal} from '@angular/core';
|
||||||
import {ActivatedRoute, RouterLink, RouterOutlet} from '@angular/router';
|
import {ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||||
import {Chat} from '@chatenium/chatenium-sdk/domain/chatService.schema';
|
import {Chat} from '@chatenium/chatenium-sdk/domain/chatService.schema';
|
||||||
import {Message} from '@chatenium/chatenium-sdk/domain/dmService.schema';
|
import {Message} from '@chatenium/chatenium-sdk/domain/dmService.schema';
|
||||||
import {MessageBoxViewModel} from '../elements/message-box/message-box-viewmodel';
|
import {MessageBoxViewModel} from '../elements/message-box/message-box-viewmodel';
|
||||||
@@ -31,6 +31,7 @@ export class Network implements OnInit {
|
|||||||
route = inject(ActivatedRoute)
|
route = inject(ActivatedRoute)
|
||||||
indexedDb = inject(IndexedDB)
|
indexedDb = inject(IndexedDB)
|
||||||
breakpoint = inject(TUI_BREAKPOINT)
|
breakpoint = inject(TUI_BREAKPOINT)
|
||||||
|
router = inject(Router)
|
||||||
|
|
||||||
routerOutletActive = signal(false)
|
routerOutletActive = signal(false)
|
||||||
|
|
||||||
|
|||||||
16
src/app/chat/picture-list/picture-list.html
Normal file
16
src/app/chat/picture-list/picture-list.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<button (click)="router.navigate(['/chat/picture/'+serviceManager.currentSession()!.userData.userid])" tuiButton [appearance]="router.url == '/chat/picture/' + serviceManager.currentSession()!.userData.userid ? 'primary' : 'secondary'" iconStart="@tui.user">
|
||||||
|
{{"chat.chatnav.pictureList.myPictures"|translate}}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@for (chat of serviceManager.chats(); track chat.chatid) {
|
||||||
|
<button [class.enlarge]="breakpoint() == 'mobile'" tuiButton [appearance]="router.url == '/chat/picture/' + chat.userid ? 'primary' : 'flat'" [routerLink]="'/chat/picture/' + chat.userid">
|
||||||
|
<oimg [src]="chat.pfp" height="35px" width="35px" [radius]="10"></oimg>
|
||||||
|
<div class="info">
|
||||||
|
@if (chat.displayName == "") {
|
||||||
|
<span>{{'@'+chat.username}}</span>
|
||||||
|
} @else {
|
||||||
|
<span>{{chat.displayName}}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
28
src/app/chat/picture-list/picture-list.scss
Normal file
28
src/app/chat/picture-list/picture-list.scss
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&.enlarge {
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: start;
|
||||||
|
|
||||||
|
.latest_message {
|
||||||
|
margin-top: -5px;
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app/chat/picture-list/picture-list.spec.ts
Normal file
22
src/app/chat/picture-list/picture-list.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PictureList } from './picture-list';
|
||||||
|
|
||||||
|
describe('PictureList', () => {
|
||||||
|
let component: PictureList;
|
||||||
|
let fixture: ComponentFixture<PictureList>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [PictureList],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(PictureList);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
41
src/app/chat/picture-list/picture-list.ts
Normal file
41
src/app/chat/picture-list/picture-list.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import {Component, inject, input, OnInit, signal} from '@angular/core';
|
||||||
|
import {ChatService} from '@chatenium/chatenium-sdk/services/chatService';
|
||||||
|
import {IndexedDB} from '../../storage/indexed-db';
|
||||||
|
import {Chat} from '@chatenium/chatenium-sdk/domain/chatService.schema';
|
||||||
|
import {TUI_BREAKPOINT, TuiButton} from '@taiga-ui/core';
|
||||||
|
import {Oimg} from '../elements/oimg/oimg';
|
||||||
|
import {Router, RouterLink} from '@angular/router';
|
||||||
|
import {TranslatePipe} from '@ngx-translate/core';
|
||||||
|
import {LoadStatus, ServiceManager} from '../../service-manager';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-picture-list',
|
||||||
|
imports: [
|
||||||
|
TuiButton,
|
||||||
|
Oimg,
|
||||||
|
RouterLink,
|
||||||
|
TranslatePipe
|
||||||
|
],
|
||||||
|
templateUrl: './picture-list.html',
|
||||||
|
styleUrl: './picture-list.scss',
|
||||||
|
})
|
||||||
|
export class PictureList implements OnInit {
|
||||||
|
userid = input<string>("")
|
||||||
|
token = input<string>("")
|
||||||
|
|
||||||
|
indexedDb = inject(IndexedDB)
|
||||||
|
router = inject(Router)
|
||||||
|
serviceManager = inject(ServiceManager)
|
||||||
|
breakpoint = inject(TUI_BREAKPOINT)
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.serviceManager.chatService = new ChatService(this.userid(), this.token(), this.indexedDb.getApi(), () => {})
|
||||||
|
try {
|
||||||
|
this.serviceManager.chats.set(await this.serviceManager.chatService.get())
|
||||||
|
this.serviceManager.chatsStatus.set(LoadStatus.loaded)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
this.serviceManager.chatsStatus.set(LoadStatus.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/app/chat/picture/see/see.html
Normal file
61
src/app/chat/picture/see/see.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<ng-template [(tuiDialog)]="viewerOpen">
|
||||||
|
<oimg [src]="viewedPicture!.path" height="100%" width="100%" [radius]="0"></oimg>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
@defer (when store) {
|
||||||
|
<navbar backButtonDest="/chat">
|
||||||
|
<div class="data">
|
||||||
|
<oimg [src]="store.uploaderData().pfp" height="50px" width="50px" [radius]="15"></oimg>
|
||||||
|
<div class="uploader-data">
|
||||||
|
@if (!store.uploaderData().displayName) {
|
||||||
|
<span class="main-name">{{'@'+store.uploaderData().username}}</span>
|
||||||
|
} @else {
|
||||||
|
<span class="main-name">{{store.uploaderData().displayName}}</span>
|
||||||
|
<span class="alt-name">{{'@'+store.uploaderData().username}}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="items-right">
|
||||||
|
</div>
|
||||||
|
</navbar>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
@if (openedAlbum) {
|
||||||
|
<div style="display: flex; align-items: center; gap: 10px">
|
||||||
|
<button tuiButton appearance="flat" iconStart="@tui.chevron-left" (click)="exitAlbum()"></button>
|
||||||
|
<h1 style="padding: 0; margin: 0">{{openedAlbum.name}}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="pictureList">
|
||||||
|
@for (picture of openedAlbum.images; track picture) {
|
||||||
|
<oimg (click)="viewPicture(picture)" [src]="picture.path" height="200px" width="200px" [radius]="25" objectFit="none"></oimg>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
@if (store.albums().length == 0) {
|
||||||
|
<tui-block-status style="height: 300px">
|
||||||
|
<tui-icon icon="@tui.frown" tuiSlot="top" style="font-size: 150px"></tui-icon>
|
||||||
|
<h4>{{"chat.pictures.albumList.noAlbum"|translate}}</h4>
|
||||||
|
</tui-block-status>
|
||||||
|
} @else {
|
||||||
|
<div id="albumList">
|
||||||
|
@for (album of store.albums(); track album) {
|
||||||
|
<div class="album" (click)="enterAlbum(album.albumId)">
|
||||||
|
<masonry [maxColSize]="3" style="height: 300px; pointer-events: none;">
|
||||||
|
@for (file of album.images; track file) {
|
||||||
|
<img [src]="file.path+'_thumbnail.png'" style="filter: blur(5px)"/>
|
||||||
|
}
|
||||||
|
</masonry>
|
||||||
|
<div class="album-name">
|
||||||
|
<h2>{{album.name}}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
85
src/app/chat/picture/see/see.scss
Normal file
85
src/app/chat/picture/see/see.scss
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
main {
|
||||||
|
height: 98svh;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 70px minmax(0, 1fr) auto;
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
navbar {
|
||||||
|
.uploader-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.main-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alt-name {
|
||||||
|
margin-top: -5px;
|
||||||
|
color: gray;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-right {
|
||||||
|
margin-top: -10px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#albumList {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: flex-start;
|
||||||
|
|
||||||
|
.album {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
background: var(--tui-background-base-alt);
|
||||||
|
border: 2px solid var(--tui-border-normal);
|
||||||
|
border-radius: 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.album-name {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: color-mix(in srgb, var(--tui-background-base) 50%, transparent);
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pictureList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-content: flex-start;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
oimg {
|
||||||
|
background: var(--tui-background-base-alt);
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app/chat/picture/see/see.spec.ts
Normal file
22
src/app/chat/picture/see/see.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { See } from './see';
|
||||||
|
|
||||||
|
describe('See', () => {
|
||||||
|
let component: See;
|
||||||
|
let fixture: ComponentFixture<See>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [See],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(See);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
133
src/app/chat/picture/see/see.ts
Normal file
133
src/app/chat/picture/see/see.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import {Component, inject, signal} from '@angular/core';
|
||||||
|
import {DmStorage, PictureStorage, ServiceManager} from '../../../service-manager';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
import {IndexedDB} from '../../../storage/indexed-db';
|
||||||
|
import {TUI_BREAKPOINT, TuiButton, TuiDialog, TuiIcon} from '@taiga-ui/core';
|
||||||
|
import {PictureService} from '@chatenium/chatenium-sdk/services/pictureService';
|
||||||
|
import {Navbar} from '../../elements/navbar/navbar';
|
||||||
|
import {Oimg} from '../../elements/oimg/oimg';
|
||||||
|
import {Masonry} from '../../elements/masonry/masonry';
|
||||||
|
import {Album, Image} from '@chatenium/chatenium-sdk/domain/pictureService.schema';
|
||||||
|
import {pictureHandlers} from '@chatenium/chatenium-sdk/mocks/handlers/picture.http';
|
||||||
|
import {TuiBlockStatusComponent} from '@taiga-ui/layout';
|
||||||
|
import {TranslatePipe} from '@ngx-translate/core';
|
||||||
|
import {TuiBadgedContentDirective} from '@taiga-ui/kit';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-see',
|
||||||
|
imports: [
|
||||||
|
Navbar,
|
||||||
|
Oimg,
|
||||||
|
TuiButton,
|
||||||
|
TuiIcon,
|
||||||
|
Masonry,
|
||||||
|
TuiDialog,
|
||||||
|
TuiBlockStatusComponent,
|
||||||
|
TranslatePipe,
|
||||||
|
TuiBadgedContentDirective
|
||||||
|
],
|
||||||
|
templateUrl: './see.html',
|
||||||
|
styleUrl: './see.scss',
|
||||||
|
})
|
||||||
|
export class See {
|
||||||
|
serviceManager = inject(ServiceManager)
|
||||||
|
route = inject(ActivatedRoute)
|
||||||
|
indexedDb = inject(IndexedDB)
|
||||||
|
breakpoint = inject(TUI_BREAKPOINT)
|
||||||
|
router = inject(Router)
|
||||||
|
|
||||||
|
uploaderId = ""
|
||||||
|
openedAlbum: Album | null = null
|
||||||
|
|
||||||
|
viewedPicture: Image | null = null
|
||||||
|
get viewerOpen() {
|
||||||
|
return this.viewedPicture != null
|
||||||
|
}
|
||||||
|
set viewerOpen(_: any) {
|
||||||
|
this.viewedPicture = null
|
||||||
|
this.router.navigate([], {
|
||||||
|
queryParams: { pictureId: null },
|
||||||
|
queryParamsHandling: "merge"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get store(): PictureStorage {
|
||||||
|
return this.serviceManager.pictureServices()[this.uploaderId]
|
||||||
|
}
|
||||||
|
|
||||||
|
enterAlbum(albumId: string) {
|
||||||
|
this.router.navigate([], {
|
||||||
|
queryParams: { albumId: albumId },
|
||||||
|
queryParamsHandling: "merge"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exitAlbum() {
|
||||||
|
this.openedAlbum = null
|
||||||
|
this.router.navigate([], {
|
||||||
|
queryParams: { albumId: null },
|
||||||
|
queryParamsHandling: "merge"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
viewPicture(picture: Image) {
|
||||||
|
this.viewedPicture = picture
|
||||||
|
this.router.navigate([], {
|
||||||
|
queryParams: { pictureId: picture.imageId },
|
||||||
|
queryParamsHandling: "merge"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.params.subscribe(async params => {
|
||||||
|
const uploaderId = params['uploaderId'];
|
||||||
|
this.uploaderId = uploaderId;
|
||||||
|
|
||||||
|
const session = this.serviceManager.currentSession();
|
||||||
|
if (!session) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.serviceManager.pictureServices()[uploaderId]) {
|
||||||
|
const newService = new PictureService(
|
||||||
|
session.token,
|
||||||
|
uploaderId,
|
||||||
|
session.userData.userid,
|
||||||
|
this.indexedDb.getApi(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const uploaderInfo = await newService.get()
|
||||||
|
|
||||||
|
this.serviceManager.pictureServices.update(services => ({
|
||||||
|
...services,
|
||||||
|
[uploaderId]: {
|
||||||
|
albums: signal(uploaderInfo.pictures),
|
||||||
|
uploaderData: signal(uploaderInfo.userData),
|
||||||
|
service: newService,
|
||||||
|
} as PictureStorage
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Show album
|
||||||
|
this.route.queryParams.subscribe(params => {
|
||||||
|
const albumId = params['albumId'];
|
||||||
|
if (albumId) {
|
||||||
|
const album = uploaderInfo.pictures.find(a => a.albumId === albumId)
|
||||||
|
if (album) {
|
||||||
|
this.openedAlbum = album
|
||||||
|
|
||||||
|
const pictureId = params['pictureId'];
|
||||||
|
if (pictureId) {
|
||||||
|
const picture = album.images.find(img => img.imageId === pictureId)
|
||||||
|
if (picture) {
|
||||||
|
this.viewedPicture = picture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly pictureHandlers = pictureHandlers;
|
||||||
|
}
|
||||||
@@ -13,6 +13,9 @@ import {NetworkService} from '@chatenium/chatenium-sdk/services/networkService';
|
|||||||
import {Network, NetworkCategory, NetworkChannel} from '@chatenium/chatenium-sdk/domain/networkService.schema';
|
import {Network, NetworkCategory, NetworkChannel} from '@chatenium/chatenium-sdk/domain/networkService.schema';
|
||||||
import {TextChannelServiceService} from '@chatenium/chatenium-sdk/services/textChannelService';
|
import {TextChannelServiceService} from '@chatenium/chatenium-sdk/services/textChannelService';
|
||||||
import {Message as NetworkMessage} from '@chatenium/chatenium-sdk/domain/textChannelService.schema';
|
import {Message as NetworkMessage} from '@chatenium/chatenium-sdk/domain/textChannelService.schema';
|
||||||
|
import {PictureService} from '@chatenium/chatenium-sdk/services/pictureService';
|
||||||
|
import {Album} from '@chatenium/chatenium-sdk/domain/pictureService.schema';
|
||||||
|
import {PublicUserData} from '@chatenium/chatenium-sdk/domain/common.schema';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -25,7 +28,7 @@ export class ServiceManager {
|
|||||||
sessionManager = new SessionManager(this.database.getApi(), this.keyring.getApi(), this.keyValue.getApi())
|
sessionManager = new SessionManager(this.database.getApi(), this.keyring.getApi(), this.keyValue.getApi())
|
||||||
currentSession = signal<Session | null>(null)
|
currentSession = signal<Session | null>(null)
|
||||||
|
|
||||||
chatService: ChatService | null = null // Initialized in dm-list.ts
|
chatService: ChatService | null = null // Initialized in picture-list.ts
|
||||||
chatsStatus = signal<LoadStatus>(LoadStatus.loading)
|
chatsStatus = signal<LoadStatus>(LoadStatus.loading)
|
||||||
chats = signal<Chat[]>([])
|
chats = signal<Chat[]>([])
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@ export class ServiceManager {
|
|||||||
networkServices = signal<Record<string, NetworkStorage>>({})
|
networkServices = signal<Record<string, NetworkStorage>>({})
|
||||||
|
|
||||||
dmServices = signal<Record<string, DmStorage>>({})
|
dmServices = signal<Record<string, DmStorage>>({})
|
||||||
|
pictureServices = signal<Record<string, PictureStorage>>({})
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum LoadStatus {
|
export enum LoadStatus {
|
||||||
@@ -53,6 +57,12 @@ export interface DmStorage {
|
|||||||
wsListener: (action: string, message: string) => void
|
wsListener: (action: string, message: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PictureStorage {
|
||||||
|
service: PictureService
|
||||||
|
albums: WritableSignal<Album[]>
|
||||||
|
uploaderData: WritableSignal<PublicUserData>
|
||||||
|
}
|
||||||
|
|
||||||
export interface NetworkStorage {
|
export interface NetworkStorage {
|
||||||
service: NetworkService
|
service: NetworkService
|
||||||
networkData: WritableSignal<Network>
|
networkData: WritableSignal<Network>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {DatabaseAPI} from '@chatenium/chatenium-sdk/storage/database';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class IndexedDB {
|
export class IndexedDB {
|
||||||
private dbVersion = 2
|
private dbVersion = 3
|
||||||
private db: IDBDatabase | null = null
|
private db: IDBDatabase | null = null
|
||||||
|
|
||||||
getApi(): DatabaseAPI {
|
getApi(): DatabaseAPI {
|
||||||
@@ -36,6 +36,7 @@ export class IndexedDB {
|
|||||||
db.createObjectStore('files', { keyPath: 'id' })
|
db.createObjectStore('files', { keyPath: 'id' })
|
||||||
db.createObjectStore('messages', { keyPath: 'id' })
|
db.createObjectStore('messages', { keyPath: 'id' })
|
||||||
db.createObjectStore('networkmessages', { keyPath: 'id' })
|
db.createObjectStore('networkmessages', { keyPath: 'id' })
|
||||||
|
db.createObjectStore('pictures', { keyPath: 'id' })
|
||||||
}
|
}
|
||||||
|
|
||||||
request.onsuccess = (event: Event) => {
|
request.onsuccess = (event: Event) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
version: "3.0-beta4",
|
version: "3.0-beta5",
|
||||||
api_url: "http://localhost:3000",
|
api_url: "http://localhost:3000",
|
||||||
cdn_url: "http://localhost:4000",
|
cdn_url: "http://localhost:4000",
|
||||||
ws_url: "ws://localhost:3000",
|
ws_url: "ws://localhost:3000",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
version: "3.0-beta4",
|
version: "3.0-beta5",
|
||||||
api_url: "https://api.chatenium.hu",
|
api_url: "https://api.chatenium.hu",
|
||||||
cdn_url: "https://cdn.chatenium.hu",
|
cdn_url: "https://cdn.chatenium.hu",
|
||||||
ws_url: "wss://api.chatenium.hu",
|
ws_url: "wss://api.chatenium.hu",
|
||||||
|
|||||||
Reference in New Issue
Block a user