├── .gitignore
├── ChatAppClient
├── .editorconfig
├── .gitignore
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.ts
│ │ ├── app.config.ts
│ │ ├── app.routes.ts
│ │ ├── auth.service.ts
│ │ ├── components
│ │ │ ├── home
│ │ │ │ ├── home.component.css
│ │ │ │ ├── home.component.html
│ │ │ │ └── home.component.ts
│ │ │ ├── login
│ │ │ │ ├── login.component.css
│ │ │ │ ├── login.component.html
│ │ │ │ └── login.component.ts
│ │ │ └── register
│ │ │ │ ├── register.component.css
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ └── models
│ │ │ ├── chat.model.ts
│ │ │ ├── register.model.ts
│ │ │ └── user.model.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ └── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
├── ChatAppServer
├── ChatAppServer.WebAPI
│ ├── ChatAppServer.WebAPI.csproj
│ ├── ChatAppServer.WebAPI.http
│ ├── Context
│ │ └── ApplicationDbContext.cs
│ ├── Controllers
│ │ ├── AuthController.cs
│ │ └── ChatsController.cs
│ ├── Dtos
│ │ ├── RegisterDto.cs
│ │ └── SendMessageDto.cs
│ ├── Hubs
│ │ └── ChatHub.cs
│ ├── Migrations
│ │ ├── 20240430090127_mg1.Designer.cs
│ │ ├── 20240430090127_mg1.cs
│ │ ├── 20240430090342_mg2.Designer.cs
│ │ ├── 20240430090342_mg2.cs
│ │ └── ApplicationDbContextModelSnapshot.cs
│ ├── Models
│ │ ├── Chat.cs
│ │ └── User.cs
│ ├── Program.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── wwwroot
│ │ └── avatar
│ │ ├── 03cf04de-6ecb-4e43-b0b1-f828c805c71b.png
│ │ ├── 17beaeac-4323-4f77-90e6-15a3e7711a15.png
│ │ ├── ca04e629-ebbf-4f81-acd8-1616864954a0.png
│ │ └── e1eb1e28-0987-4c22-877a-39cb86f955bc.png
└── ChatAppServer.sln
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore Visual Studio temporary files, build results, and
2 | # files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.rsuser
6 | *.suo
7 | *.user
8 | *.userosscache
9 | *.sln.docstates
10 |
11 | # User-specific files (MonoDevelop/Xamarin Studio)
12 | *.userprefs
13 |
14 | # Mono Auto Generated Files
15 | mono_crash.*
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | [Aa][Rr][Mm]/
25 | [Aa][Rr][Mm]64/
26 | bld/
27 | [Bb]in/
28 | [Oo]bj/
29 | [Ll]og/
30 | [Ll]ogs/
31 |
32 | # Visual Studio 2015/2017 cache/options directory
33 | .vs/
34 | # Uncomment if you have tasks that create the project's static files in wwwroot
35 | #wwwroot/
36 |
37 | # MSTest test Results
38 | [Tt]est[Rr]esult*/
39 | [Bb]uild[Ll]og.*
40 |
41 | # NUnit
42 | *.VisualState.xml
43 | TestResult.xml
44 | nunit-*.xml
45 |
46 | # Build Results of an MSBuild Project
47 | *_i.c
48 | *_p.c
49 | *_h.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.iobj
54 | *.pch
55 | *.pdb
56 | *.ipdb
57 | *.pgc
58 | *.pgd
59 | *.rsp
60 | *.sbr
61 | *.tlb
62 | *.tli
63 | *.tlh
64 | *.tmp
65 | *.tmp_proj
66 | *.log
67 | *.vspscc
68 | *.vssscc
69 | *.user
70 | *.msscc
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.sdf
81 | *.opensdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # Visual Studio Trace Files
91 | *.e2e
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # AxoCover
114 | .axoCover/*
115 | !.axoCover/settings.json
116 |
117 | # Visual Studio code coverage results
118 | *.coverage
119 | *.coveragexml
120 |
121 | # NCrunch
122 | _NCrunch_*
123 | .*crunch*.local.xml
124 | nCrunchTemp_*
125 |
126 | # MightyMoose
127 | *.mm.*
128 | AutoTest.Net/
129 |
130 | # BizTalk temporary files
131 | *.PDB
132 | *.pfx
133 | *.publishsettings
134 |
135 | # Web workbench (sass)
136 | .sass-cache/
137 |
138 | # Installshield output folder
139 | [Ee]xpress/
140 | # DocProject is a documentation generator add-in
141 | DocProject/buildhelp/
142 | DocProject/Help/*.HxT
143 | DocProject/Help/*.HxC
144 | DocProject/Help/*.HHK
145 | DocProject/Help/*.HHP
146 | DocProject/HTMLHelp_Workshop/
147 |
148 | # Click-Once directory
149 | publish/
150 |
151 | # Publish Web Output
152 | *.Publish.xml
153 | *.pubxml
154 | *.publishproj
155 |
156 | # NuGet Packages Directory
157 | *.nupkg
158 | # The packages folder can be ignored because of Package Restore
159 | **/[Pp]ackages/*
160 | *.nuget
161 |
162 | # Windows Azure Build Output
163 | csx/
164 | *.build.csdef
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 |
169 | # Others
170 | sql/
171 | *.Cache
172 | ClientBin/
173 | [Ss]tyle[Cc]op.*
174 | ~$*
175 | *~
176 | *.dbmdl
177 | *.dbproj.schemaview
178 | *.jfm
179 | *.log
180 | *.pgml
181 | *.mdf
182 | *.ldf
183 | mdbackup/
184 | *.tscache
185 | *.tfstate
186 | *.tfstate.backup
187 | *.bak
188 | *.bacpac
189 | databasedevelopment.md
190 | *.mdf
191 | *.ldf
192 | *_Database*.*
193 | *.scc
194 | [node_modules]
195 | [node_modules/**]
196 | /history/*
197 | **/site
198 | **/properties
199 | *.epp
200 | *.ini
201 | *.swp
202 | *.ctp
203 | *.bak.*
204 | *.swx
205 | *.mdl
206 | *.svj
207 | *.geo
208 | *.dat
209 | *.swp
210 | *.bk
--------------------------------------------------------------------------------
/ChatAppClient/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/ChatAppClient/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/ChatAppClient/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3 | "recommendations": ["angular.ng-template"]
4 | }
5 |
--------------------------------------------------------------------------------
/ChatAppClient/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "name": "ng serve",
7 | "type": "chrome",
8 | "request": "launch",
9 | "preLaunchTask": "npm: start",
10 | "url": "http://localhost:4200/"
11 | },
12 | {
13 | "name": "ng test",
14 | "type": "chrome",
15 | "request": "launch",
16 | "preLaunchTask": "npm: test",
17 | "url": "http://localhost:9876/debug.html"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/ChatAppClient/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "type": "npm",
7 | "script": "start",
8 | "isBackground": true,
9 | "problemMatcher": {
10 | "owner": "typescript",
11 | "pattern": "$tsc",
12 | "background": {
13 | "activeOnStart": true,
14 | "beginsPattern": {
15 | "regexp": "(.*?)"
16 | },
17 | "endsPattern": {
18 | "regexp": "bundle generation complete"
19 | }
20 | }
21 | }
22 | },
23 | {
24 | "type": "npm",
25 | "script": "test",
26 | "isBackground": true,
27 | "problemMatcher": {
28 | "owner": "typescript",
29 | "pattern": "$tsc",
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": {
33 | "regexp": "(.*?)"
34 | },
35 | "endsPattern": {
36 | "regexp": "bundle generation complete"
37 | }
38 | }
39 | }
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/ChatAppClient/README.md:
--------------------------------------------------------------------------------
1 | # ChatAppClient
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.5.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28 |
--------------------------------------------------------------------------------
/ChatAppClient/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ChatAppClient": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:class": {
10 | "skipTests": true
11 | },
12 | "@schematics/angular:component": {
13 | "skipTests": true
14 | },
15 | "@schematics/angular:directive": {
16 | "skipTests": true
17 | },
18 | "@schematics/angular:guard": {
19 | "skipTests": true
20 | },
21 | "@schematics/angular:interceptor": {
22 | "skipTests": true
23 | },
24 | "@schematics/angular:pipe": {
25 | "skipTests": true
26 | },
27 | "@schematics/angular:resolver": {
28 | "skipTests": true
29 | },
30 | "@schematics/angular:service": {
31 | "skipTests": true
32 | }
33 | },
34 | "root": "",
35 | "sourceRoot": "src",
36 | "prefix": "app",
37 | "architect": {
38 | "build": {
39 | "builder": "@angular-devkit/build-angular:application",
40 | "options": {
41 | "outputPath": "dist/chat-app-client",
42 | "index": "src/index.html",
43 | "browser": "src/main.ts",
44 | "polyfills": [
45 | "zone.js"
46 | ],
47 | "tsConfig": "tsconfig.app.json",
48 | "assets": [
49 | "src/favicon.ico",
50 | "src/assets"
51 | ],
52 | "styles": [
53 | "src/styles.css"
54 | ],
55 | "scripts": []
56 | },
57 | "configurations": {
58 | "production": {
59 | "budgets": [
60 | {
61 | "type": "initial",
62 | "maximumWarning": "500kb",
63 | "maximumError": "1mb"
64 | },
65 | {
66 | "type": "anyComponentStyle",
67 | "maximumWarning": "2kb",
68 | "maximumError": "4kb"
69 | }
70 | ],
71 | "outputHashing": "all"
72 | },
73 | "development": {
74 | "optimization": false,
75 | "extractLicenses": false,
76 | "sourceMap": true
77 | }
78 | },
79 | "defaultConfiguration": "production"
80 | },
81 | "serve": {
82 | "builder": "@angular-devkit/build-angular:dev-server",
83 | "configurations": {
84 | "production": {
85 | "buildTarget": "ChatAppClient:build:production"
86 | },
87 | "development": {
88 | "buildTarget": "ChatAppClient:build:development"
89 | }
90 | },
91 | "defaultConfiguration": "development"
92 | },
93 | "extract-i18n": {
94 | "builder": "@angular-devkit/build-angular:extract-i18n",
95 | "options": {
96 | "buildTarget": "ChatAppClient:build"
97 | }
98 | },
99 | "test": {
100 | "builder": "@angular-devkit/build-angular:karma",
101 | "options": {
102 | "polyfills": [
103 | "zone.js",
104 | "zone.js/testing"
105 | ],
106 | "tsConfig": "tsconfig.spec.json",
107 | "assets": [
108 | "src/favicon.ico",
109 | "src/assets"
110 | ],
111 | "styles": [
112 | "src/styles.css"
113 | ],
114 | "scripts": []
115 | }
116 | }
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/ChatAppClient/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chat-app-client",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "watch": "ng build --watch --configuration development",
9 | "test": "ng test"
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@angular/animations": "^17.3.0",
14 | "@angular/common": "^17.3.0",
15 | "@angular/compiler": "^17.3.0",
16 | "@angular/core": "^17.3.0",
17 | "@angular/forms": "^17.3.0",
18 | "@angular/platform-browser": "^17.3.0",
19 | "@angular/platform-browser-dynamic": "^17.3.0",
20 | "@angular/router": "^17.3.0",
21 | "@microsoft/signalr": "^8.0.0",
22 | "rxjs": "~7.8.0",
23 | "tslib": "^2.3.0",
24 | "zone.js": "~0.14.3"
25 | },
26 | "devDependencies": {
27 | "@angular-devkit/build-angular": "^17.3.5",
28 | "@angular/cli": "^17.3.5",
29 | "@angular/compiler-cli": "^17.3.0",
30 | "@types/jasmine": "~5.1.0",
31 | "jasmine-core": "~5.1.0",
32 | "karma": "~6.4.0",
33 | "karma-chrome-launcher": "~3.2.0",
34 | "karma-coverage": "~2.2.0",
35 | "karma-jasmine": "~5.1.0",
36 | "karma-jasmine-html-reporter": "~2.1.0",
37 | "typescript": "~5.4.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppClient/src/app/app.component.css
--------------------------------------------------------------------------------
/ChatAppClient/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { Component } from '@angular/core';
3 | import { RouterOutlet } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'app-root',
7 | standalone: true,
8 | imports: [RouterOutlet],
9 | templateUrl: './app.component.html',
10 | styleUrl: './app.component.css'
11 | })
12 | export class AppComponent {
13 | }
14 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/app.config.ts:
--------------------------------------------------------------------------------
1 | import { ApplicationConfig } from '@angular/core';
2 | import { provideRouter } from '@angular/router';
3 |
4 | import { routes } from './app.routes';
5 | import { provideHttpClient } from '@angular/common/http';
6 |
7 | export const appConfig: ApplicationConfig = {
8 | providers: [provideRouter(routes), provideHttpClient()]
9 | };
10 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { HomeComponent } from './components/home/home.component';
3 | import { LoginComponent } from './components/login/login.component';
4 | import { RegisterComponent } from './components/register/register.component';
5 | import { inject } from '@angular/core';
6 | import { AuthService } from './auth.service';
7 |
8 | export const routes: Routes = [
9 | {
10 | path: "",
11 | component: HomeComponent,
12 | canActivate: [()=> inject(AuthService).isAuthenticated()]
13 | },
14 | {
15 | path: "login",
16 | component: LoginComponent
17 | },
18 | {
19 | path: "register",
20 | component: RegisterComponent
21 | }
22 | ];
23 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class AuthService {
8 |
9 | constructor(private router: Router) { }
10 |
11 | isAuthenticated(){
12 | if(localStorage.getItem("accessToken")){
13 | return true;
14 | }
15 |
16 | this.router.navigateByUrl("/login");
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/home/home.component.css:
--------------------------------------------------------------------------------
1 | body{
2 | background-color: #f4f7f6;
3 | margin-top:20px;
4 | }
5 | .card {
6 | background: #fff;
7 | transition: .5s;
8 | border: 0;
9 | margin-bottom: 30px;
10 | border-radius: .55rem;
11 | position: relative;
12 | width: 100%;
13 | box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
14 | }
15 | .chat-app .people-list {
16 | width: 280px;
17 | position: absolute;
18 | left: 0;
19 | top: 0;
20 | padding: 20px;
21 | z-index: 7
22 | }
23 |
24 | .chat-app .chat {
25 | margin-left: 280px;
26 | border-left: 1px solid #eaeaea
27 | }
28 |
29 | .people-list {
30 | -moz-transition: .5s;
31 | -o-transition: .5s;
32 | -webkit-transition: .5s;
33 | transition: .5s
34 | }
35 |
36 | .people-list .chat-list li {
37 | padding: 10px 15px;
38 | list-style: none;
39 | border-radius: 3px
40 | }
41 |
42 | .people-list .chat-list li:hover {
43 | background: #efefef;
44 | cursor: pointer
45 | }
46 |
47 | .people-list .chat-list li.active {
48 | background: #efefef
49 | }
50 |
51 | .people-list .chat-list li .name {
52 | font-size: 15px
53 | }
54 |
55 | .people-list .chat-list img {
56 | width: 45px;
57 | border-radius: 50%
58 | }
59 |
60 | .people-list img {
61 | float: left;
62 | border-radius: 50%
63 | }
64 |
65 | .people-list .about {
66 | float: left;
67 | padding-left: 8px
68 | }
69 |
70 | .people-list .status {
71 | color: #999;
72 | font-size: 13px
73 | }
74 |
75 | .chat .chat-header {
76 | padding: 15px 20px;
77 | border-bottom: 2px solid #f4f7f6
78 | }
79 |
80 | .chat .chat-header img {
81 | float: left;
82 | border-radius: 40px;
83 | width: 40px
84 | }
85 |
86 | .chat .chat-header .chat-about {
87 | float: left;
88 | padding-left: 10px
89 | }
90 |
91 | .chat .chat-history {
92 | padding: 20px;
93 | border-bottom: 2px solid #fff
94 | }
95 |
96 | .chat .chat-history ul {
97 | padding: 0
98 | }
99 |
100 | .chat .chat-history ul li {
101 | list-style: none;
102 | margin-bottom: 30px
103 | }
104 |
105 | .chat .chat-history ul li:last-child {
106 | margin-bottom: 0px
107 | }
108 |
109 | .chat .chat-history .message-data {
110 | margin-bottom: 15px
111 | }
112 |
113 | .chat .chat-history .message-data img {
114 | border-radius: 40px;
115 | width: 40px
116 | }
117 |
118 | .chat .chat-history .message-data-time {
119 | color: #434651;
120 | padding-left: 6px
121 | }
122 |
123 | .chat .chat-history .message {
124 | color: #444;
125 | padding: 18px 20px;
126 | line-height: 26px;
127 | font-size: 16px;
128 | border-radius: 7px;
129 | display: inline-block;
130 | position: relative
131 | }
132 |
133 | .chat .chat-history .message:after {
134 | bottom: 100%;
135 | left: 7%;
136 | border: solid transparent;
137 | content: " ";
138 | height: 0;
139 | width: 0;
140 | position: absolute;
141 | pointer-events: none;
142 | border-bottom-color: #fff;
143 | border-width: 10px;
144 | margin-left: -10px
145 | }
146 |
147 | .chat .chat-history .my-message {
148 | background: #efefef
149 | }
150 |
151 | .chat .chat-history .my-message:after {
152 | bottom: 100%;
153 | left: 30px;
154 | border: solid transparent;
155 | content: " ";
156 | height: 0;
157 | width: 0;
158 | position: absolute;
159 | pointer-events: none;
160 | border-bottom-color: #efefef;
161 | border-width: 10px;
162 | margin-left: -10px
163 | }
164 |
165 | .chat .chat-history .other-message {
166 | background: #e8f1f3;
167 | text-align: right
168 | }
169 |
170 | .chat .chat-history .other-message:after {
171 | border-bottom-color: #e8f1f3;
172 | left: 93%
173 | }
174 |
175 | .chat .chat-message {
176 | padding: 20px
177 | }
178 |
179 | .online,
180 | .offline,
181 | .me {
182 | margin-right: 2px;
183 | font-size: 8px;
184 | vertical-align: middle
185 | }
186 |
187 | .online {
188 | color: #86c541
189 | }
190 |
191 | .offline {
192 | color: #e47297
193 | }
194 |
195 | .me {
196 | color: #1d8ecd
197 | }
198 |
199 | .float-right {
200 | float: right
201 | }
202 |
203 | .clearfix:after {
204 | visibility: hidden;
205 | display: block;
206 | font-size: 0;
207 | content: " ";
208 | clear: both;
209 | height: 0
210 | }
211 |
212 | @media only screen and (max-width: 767px) {
213 | .chat-app .people-list {
214 | height: 465px;
215 | width: 100%;
216 | overflow-x: auto;
217 | background: #fff;
218 | left: -400px;
219 | display: none
220 | }
221 | .chat-app .people-list.open {
222 | left: 0
223 | }
224 | .chat-app .chat {
225 | margin: 0
226 | }
227 | .chat-app .chat .chat-header {
228 | border-radius: 0.55rem 0.55rem 0 0
229 | }
230 | .chat-app .chat-history {
231 | height: 300px;
232 | overflow-x: auto
233 | }
234 | }
235 |
236 | @media only screen and (min-width: 768px) and (max-width: 992px) {
237 | .chat-app .chat-list {
238 | height: 650px;
239 | overflow-x: auto
240 | }
241 | .chat-app .chat-history {
242 | height: 600px;
243 | overflow-x: auto
244 | }
245 | }
246 |
247 | @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 1) {
248 | .chat-app .chat-list {
249 | height: 480px;
250 | overflow-x: auto
251 | }
252 | .chat-app .chat-history {
253 | height: calc(100vh - 350px);
254 | overflow-x: auto
255 | }
256 | }
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
TS ChatAPP
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | @for(user of users; track user){
18 | -
19 |
20 |
21 |
{{user.name}}
22 |
{{user.status}}
23 |
24 |
25 | }
26 |
27 |
28 | @if(selectedUserId){
29 |
30 |
43 |
44 |
45 | @for(chat of chats; track chat){
46 | @if(selectedUserId != chat.userId){
47 | -
48 |
49 | {{chat.date}}
50 |
51 | {{chat.message}}
52 |
53 | }@else {
54 | -
55 |
56 | {{chat.date}}
57 |
58 | {{chat.message}}
59 |
60 | }
61 | }
62 |
63 |
64 |
72 |
73 | }@else {}
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { Component } from '@angular/core';
3 | import { UserModel } from '../../models/user.model';
4 | import { ChatModel } from '../../models/chat.model';
5 | import { HttpClient } from '@angular/common/http';
6 | import * as signalR from '@microsoft/signalr';
7 | import { FormsModule } from '@angular/forms';
8 |
9 | @Component({
10 | selector: 'app-home',
11 | standalone: true,
12 | imports: [CommonModule,FormsModule],
13 | templateUrl: './home.component.html',
14 | styleUrl: './home.component.css'
15 | })
16 | export class HomeComponent {
17 | users: UserModel[] = [];
18 | chats: ChatModel[] = [];
19 | selectedUserId: string = "";
20 | selectedUser: UserModel = new UserModel();
21 | user = new UserModel();
22 | hub: signalR.HubConnection | undefined;
23 | message: string = "";
24 |
25 | constructor(
26 | private http: HttpClient
27 | ){
28 | this.user = JSON.parse(localStorage.getItem("accessToken") ?? "");
29 | this.getUsers();
30 |
31 | this.hub = new signalR.HubConnectionBuilder().withUrl("https://localhost:7123/chat-hub").build();
32 |
33 | this.hub.start().then(()=> {
34 | console.log("Connection is started...");
35 |
36 | this.hub?.invoke("Connect", this.user.id);
37 |
38 | this.hub?.on("Users", (res:UserModel) => {
39 | console.log(res);
40 | this.users.find(p=> p.id == res.id)!.status = res.status;
41 | });
42 |
43 | this.hub?.on("Messages",(res:ChatModel)=> {
44 | console.log(res);
45 |
46 | if(this.selectedUserId == res.userId){
47 | this.chats.push(res);
48 | }
49 | })
50 | })
51 | }
52 |
53 | getUsers(){
54 | this.http.get("https://localhost:7123/api/Chats/GetUsers").subscribe(res=> {
55 | this.users = res.filter(p => p.id != this.user.id);
56 | })
57 | }
58 |
59 | changeUser(user: UserModel){
60 | this.selectedUserId = user.id;
61 | this.selectedUser = user;
62 |
63 | this.http.get(`https://localhost:7123/api/Chats/GetChats?userId=${this.user.id}&toUserId=${this.selectedUserId}`).subscribe((res:any)=>{
64 | this.chats = res;
65 | });
66 | }
67 |
68 | logout(){
69 | localStorage.clear();
70 | document.location.reload();
71 | }
72 |
73 | sendMessage(){
74 | const data ={
75 | "userId": this.user.id,
76 | "toUserId": this.selectedUserId,
77 | "message": this.message
78 | }
79 | this.http.post("https://localhost:7123/api/Chats/SendMessage",data).subscribe(
80 | (res)=> {
81 | this.chats.push(res);
82 | this.message = "";
83 | });
84 | }
85 |
86 | }
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/login/login.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppClient/src/app/components/login/login.component.css
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Component } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { Router } from '@angular/router';
5 |
6 | @Component({
7 | selector: 'app-login',
8 | standalone: true,
9 | imports: [FormsModule],
10 | templateUrl: './login.component.html',
11 | styleUrl: './login.component.css'
12 | })
13 | export class LoginComponent {
14 | name: string = "";
15 |
16 | constructor(private http: HttpClient,
17 | private router: Router
18 | ){
19 |
20 | }
21 | login(){
22 | this.http.get("https://localhost:7123/api/Auth/Login?name=" + this.name).subscribe(res=> {
23 | localStorage.setItem("accessToken", JSON.stringify(res));
24 | this.router.navigateByUrl("/");
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/register/register.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppClient/src/app/components/register/register.component.css
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/register/register.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/components/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Component } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { Router } from '@angular/router';
5 | import { RegisterModel } from '../../models/register.model';
6 |
7 | @Component({
8 | selector: 'app-register',
9 | standalone: true,
10 | imports: [FormsModule],
11 | templateUrl: './register.component.html',
12 | styleUrl: './register.component.css'
13 | })
14 | export class RegisterComponent {
15 | registerModel: RegisterModel = new RegisterModel();
16 |
17 | constructor(
18 | private http: HttpClient,
19 | private router: Router
20 | ){}
21 |
22 | setImage(event:any){
23 | this.registerModel.file = event.target.files[0];
24 | }
25 |
26 | register(){
27 | const formData = new FormData();
28 | formData.append("name", this.registerModel.name);
29 | formData.append("file", this.registerModel.file, this.registerModel.file.name);
30 |
31 | this.http.post("https://localhost:7123/api/Auth/Register", formData).subscribe(res=> {
32 | localStorage.setItem("accessToken", JSON.stringify(res));
33 | this.router.navigateByUrl("/");
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/models/chat.model.ts:
--------------------------------------------------------------------------------
1 | export class ChatModel{
2 | userId: string = "";
3 | toUserId: string = "";
4 | date: string ="";
5 | message: string = "";
6 | }
7 |
--------------------------------------------------------------------------------
/ChatAppClient/src/app/models/register.model.ts:
--------------------------------------------------------------------------------
1 | export class RegisterModel{
2 | name: string = "";
3 | file: any;
4 | }
--------------------------------------------------------------------------------
/ChatAppClient/src/app/models/user.model.ts:
--------------------------------------------------------------------------------
1 | export class UserModel{
2 | id:string = "";
3 | name: string = "";
4 | status: string = "";
5 | avatar: string = "";
6 | }
--------------------------------------------------------------------------------
/ChatAppClient/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppClient/src/assets/.gitkeep
--------------------------------------------------------------------------------
/ChatAppClient/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppClient/src/favicon.ico
--------------------------------------------------------------------------------
/ChatAppClient/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ChatAppClient
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ChatAppClient/src/main.ts:
--------------------------------------------------------------------------------
1 | import { bootstrapApplication } from '@angular/platform-browser';
2 | import { appConfig } from './app/app.config';
3 | import { AppComponent } from './app/app.component';
4 |
5 | bootstrapApplication(AppComponent, appConfig)
6 | .catch((err) => console.error(err));
7 |
--------------------------------------------------------------------------------
/ChatAppClient/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/ChatAppClient/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/ChatAppClient/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "outDir": "./dist/out-tsc",
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "sourceMap": true,
14 | "declaration": false,
15 | "experimentalDecorators": true,
16 | "moduleResolution": "node",
17 | "importHelpers": true,
18 | "target": "ES2022",
19 | "module": "ES2022",
20 | "useDefineForClassFields": false,
21 | "lib": [
22 | "ES2022",
23 | "dom"
24 | ]
25 | },
26 | "angularCompilerOptions": {
27 | "enableI18nLegacyMessageIdFormat": false,
28 | "strictInjectionParameters": true,
29 | "strictInputAccessModifiers": true,
30 | "strictTemplates": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ChatAppClient/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/ChatAppServer.WebAPI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/ChatAppServer.WebAPI.http:
--------------------------------------------------------------------------------
1 | @ChatAppServer.WebAPI_HostAddress = http://localhost:5245
2 |
3 | GET {{ChatAppServer.WebAPI_HostAddress}}/weatherforecast/
4 | Accept: application/json
5 |
6 | ###
7 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Context/ApplicationDbContext.cs:
--------------------------------------------------------------------------------
1 | using ChatAppServer.WebAPI.Models;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace ChatAppServer.WebAPI.Context;
5 |
6 | public sealed class ApplicationDbContext : DbContext
7 | {
8 | public ApplicationDbContext(DbContextOptions options) : base(options)
9 | {
10 | }
11 |
12 | public DbSet Users { get; set; }
13 | public DbSet Chats { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Controllers/AuthController.cs:
--------------------------------------------------------------------------------
1 | using ChatAppServer.WebAPI.Context;
2 | using ChatAppServer.WebAPI.Dtos;
3 | using ChatAppServer.WebAPI.Models;
4 | using GenericFileService.Files;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ChatAppServer.WebAPI.Controllers;
9 | [Route("api/[controller]/[action]")]
10 | [ApiController]
11 | public sealed class AuthController(
12 | ApplicationDbContext context) : ControllerBase
13 | {
14 | [HttpPost]
15 | public async Task Register([FromForm] RegisterDto request, CancellationToken cancellationToken)
16 | {
17 | bool isNameExists = await context.Users.AnyAsync(p => p.Name == request.Name, cancellationToken);
18 |
19 | if (isNameExists)
20 | {
21 | return BadRequest(new { Message = "Bu kullanıcı adı daha önce kullanılmış" });
22 | }
23 |
24 | string avatar = FileService.FileSaveToServer(request.File, "wwwroot/avatar/");
25 |
26 | User user = new()
27 | {
28 | Name = request.Name,
29 | Avatar = avatar
30 | };
31 |
32 | await context.AddAsync(user, cancellationToken);
33 | await context.SaveChangesAsync();
34 |
35 | return Ok(user);
36 | }
37 |
38 | [HttpGet]
39 | public async Task Login(string name, CancellationToken cancellationToken)
40 | {
41 | User? user = await context.Users.FirstOrDefaultAsync(p => p.Name == name, cancellationToken);
42 |
43 | if(user is null)
44 | {
45 | return BadRequest(new { Message = "Kullanıcı bulunamadı" });
46 | }
47 |
48 | user.Status = "online";
49 |
50 | await context.SaveChangesAsync(cancellationToken);
51 |
52 | return Ok(user);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Controllers/ChatsController.cs:
--------------------------------------------------------------------------------
1 | using ChatAppServer.WebAPI.Context;
2 | using ChatAppServer.WebAPI.Dtos;
3 | using ChatAppServer.WebAPI.Hubs;
4 | using ChatAppServer.WebAPI.Models;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.AspNetCore.SignalR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ChatAppServer.WebAPI.Controllers;
10 | [Route("api/[controller]/[action]")]
11 | [ApiController]
12 | public sealed class ChatsController(
13 | ApplicationDbContext context,
14 | IHubContext hubContext) : ControllerBase
15 | {
16 | [HttpGet]
17 | public async Task GetUsers()
18 | {
19 | List users = await context.Users.OrderBy(p => p.Name).ToListAsync();
20 | return Ok(users);
21 | }
22 |
23 | [HttpGet]
24 | public async Task GetChats(Guid userId, Guid toUserId, CancellationToken cancellationToken)
25 | {
26 | List chats =
27 | await context
28 | .Chats
29 | .Where(p =>
30 | p.UserId == userId && p.ToUserId == toUserId ||
31 | p.ToUserId == userId && p.UserId == toUserId)
32 | .OrderBy(p=> p.Date)
33 | .ToListAsync(cancellationToken);
34 |
35 | return Ok(chats);
36 | }
37 |
38 | [HttpPost]
39 | public async Task SendMessage(SendMessageDto request, CancellationToken cancellationToken)
40 | {
41 | Chat chat = new()
42 | {
43 | UserId = request.UserId,
44 | ToUserId = request.ToUserId,
45 | Message = request.Message,
46 | Date = DateTime.Now
47 | };
48 |
49 | await context.AddAsync(chat, cancellationToken);
50 | await context.SaveChangesAsync(cancellationToken);
51 |
52 | string connectionId = ChatHub.Users.First(p => p.Value == chat.ToUserId).Key;
53 |
54 | await hubContext.Clients.Client(connectionId).SendAsync("Messages", chat);
55 |
56 | return Ok(chat);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Dtos/RegisterDto.cs:
--------------------------------------------------------------------------------
1 | namespace ChatAppServer.WebAPI.Dtos;
2 |
3 | public sealed record RegisterDto(
4 | string Name,
5 | IFormFile File);
6 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Dtos/SendMessageDto.cs:
--------------------------------------------------------------------------------
1 | namespace ChatAppServer.WebAPI.Dtos;
2 |
3 | public sealed record SendMessageDto(
4 | Guid UserId,
5 | Guid ToUserId,
6 | string Message);
7 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Hubs/ChatHub.cs:
--------------------------------------------------------------------------------
1 | using ChatAppServer.WebAPI.Context;
2 | using ChatAppServer.WebAPI.Models;
3 | using Microsoft.AspNetCore.SignalR;
4 |
5 | namespace ChatAppServer.WebAPI.Hubs;
6 |
7 | public sealed class ChatHub(ApplicationDbContext context) : Hub
8 | {
9 | public static Dictionary Users = new();
10 | public async Task Connect(Guid userId)
11 | {
12 | Users.Add(Context.ConnectionId, userId);
13 | User? user = await context.Users.FindAsync(userId);
14 | if(user is not null)
15 | {
16 | user.Status = "online";
17 | await context.SaveChangesAsync();
18 |
19 | await Clients.All.SendAsync("Users", user);
20 | }
21 | }
22 |
23 | public override async Task OnDisconnectedAsync(Exception? exception)
24 | {
25 | Guid userId;
26 | Users.TryGetValue(Context.ConnectionId, out userId);
27 | Users.Remove(Context.ConnectionId);
28 | User? user = await context.Users.FindAsync(userId);
29 | if (user is not null)
30 | {
31 | user.Status = "offline";
32 | await context.SaveChangesAsync();
33 |
34 | await Clients.All.SendAsync("Users", user);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Migrations/20240430090127_mg1.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using ChatAppServer.WebAPI.Context;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.EntityFrameworkCore.Infrastructure;
5 | using Microsoft.EntityFrameworkCore.Metadata;
6 | using Microsoft.EntityFrameworkCore.Migrations;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | #nullable disable
10 |
11 | namespace ChatAppServer.WebAPI.Migrations
12 | {
13 | [DbContext(typeof(ApplicationDbContext))]
14 | [Migration("20240430090127_mg1")]
15 | partial class mg1
16 | {
17 | ///
18 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
19 | {
20 | #pragma warning disable 612, 618
21 | modelBuilder
22 | .HasAnnotation("ProductVersion", "8.0.4")
23 | .HasAnnotation("Relational:MaxIdentifierLength", 128);
24 |
25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
26 | #pragma warning restore 612, 618
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Migrations/20240430090127_mg1.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace ChatAppServer.WebAPI.Migrations
6 | {
7 | ///
8 | public partial class mg1 : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 |
14 | }
15 |
16 | ///
17 | protected override void Down(MigrationBuilder migrationBuilder)
18 | {
19 |
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Migrations/20240430090342_mg2.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using ChatAppServer.WebAPI.Context;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 |
10 | #nullable disable
11 |
12 | namespace ChatAppServer.WebAPI.Migrations
13 | {
14 | [DbContext(typeof(ApplicationDbContext))]
15 | [Migration("20240430090342_mg2")]
16 | partial class mg2
17 | {
18 | ///
19 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
20 | {
21 | #pragma warning disable 612, 618
22 | modelBuilder
23 | .HasAnnotation("ProductVersion", "8.0.4")
24 | .HasAnnotation("Relational:MaxIdentifierLength", 128);
25 |
26 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
27 |
28 | modelBuilder.Entity("ChatAppServer.WebAPI.Models.Chat", b =>
29 | {
30 | b.Property("Id")
31 | .ValueGeneratedOnAdd()
32 | .HasColumnType("uniqueidentifier");
33 |
34 | b.Property("Date")
35 | .HasColumnType("datetime2");
36 |
37 | b.Property("Message")
38 | .IsRequired()
39 | .HasColumnType("nvarchar(max)");
40 |
41 | b.Property("ToUserId")
42 | .HasColumnType("uniqueidentifier");
43 |
44 | b.Property("UserId")
45 | .HasColumnType("uniqueidentifier");
46 |
47 | b.HasKey("Id");
48 |
49 | b.ToTable("Chats");
50 | });
51 |
52 | modelBuilder.Entity("ChatAppServer.WebAPI.Models.User", b =>
53 | {
54 | b.Property("Id")
55 | .ValueGeneratedOnAdd()
56 | .HasColumnType("uniqueidentifier");
57 |
58 | b.Property("Avatar")
59 | .IsRequired()
60 | .HasColumnType("nvarchar(max)");
61 |
62 | b.Property("Name")
63 | .IsRequired()
64 | .HasColumnType("nvarchar(max)");
65 |
66 | b.Property("Status")
67 | .IsRequired()
68 | .HasColumnType("nvarchar(max)");
69 |
70 | b.HasKey("Id");
71 |
72 | b.ToTable("Users");
73 | });
74 | #pragma warning restore 612, 618
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Migrations/20240430090342_mg2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | #nullable disable
5 |
6 | namespace ChatAppServer.WebAPI.Migrations
7 | {
8 | ///
9 | public partial class mg2 : Migration
10 | {
11 | ///
12 | protected override void Up(MigrationBuilder migrationBuilder)
13 | {
14 | migrationBuilder.CreateTable(
15 | name: "Chats",
16 | columns: table => new
17 | {
18 | Id = table.Column(type: "uniqueidentifier", nullable: false),
19 | UserId = table.Column(type: "uniqueidentifier", nullable: false),
20 | ToUserId = table.Column(type: "uniqueidentifier", nullable: false),
21 | Message = table.Column(type: "nvarchar(max)", nullable: false),
22 | Date = table.Column(type: "datetime2", nullable: false)
23 | },
24 | constraints: table =>
25 | {
26 | table.PrimaryKey("PK_Chats", x => x.Id);
27 | });
28 |
29 | migrationBuilder.CreateTable(
30 | name: "Users",
31 | columns: table => new
32 | {
33 | Id = table.Column(type: "uniqueidentifier", nullable: false),
34 | Name = table.Column(type: "nvarchar(max)", nullable: false),
35 | Avatar = table.Column(type: "nvarchar(max)", nullable: false),
36 | Status = table.Column(type: "nvarchar(max)", nullable: false)
37 | },
38 | constraints: table =>
39 | {
40 | table.PrimaryKey("PK_Users", x => x.Id);
41 | });
42 | }
43 |
44 | ///
45 | protected override void Down(MigrationBuilder migrationBuilder)
46 | {
47 | migrationBuilder.DropTable(
48 | name: "Chats");
49 |
50 | migrationBuilder.DropTable(
51 | name: "Users");
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Migrations/ApplicationDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using ChatAppServer.WebAPI.Context;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | #nullable disable
10 |
11 | namespace ChatAppServer.WebAPI.Migrations
12 | {
13 | [DbContext(typeof(ApplicationDbContext))]
14 | partial class ApplicationDbContextModelSnapshot : ModelSnapshot
15 | {
16 | protected override void BuildModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasAnnotation("ProductVersion", "8.0.4")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 128);
22 |
23 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
24 |
25 | modelBuilder.Entity("ChatAppServer.WebAPI.Models.Chat", b =>
26 | {
27 | b.Property("Id")
28 | .ValueGeneratedOnAdd()
29 | .HasColumnType("uniqueidentifier");
30 |
31 | b.Property("Date")
32 | .HasColumnType("datetime2");
33 |
34 | b.Property("Message")
35 | .IsRequired()
36 | .HasColumnType("nvarchar(max)");
37 |
38 | b.Property("ToUserId")
39 | .HasColumnType("uniqueidentifier");
40 |
41 | b.Property("UserId")
42 | .HasColumnType("uniqueidentifier");
43 |
44 | b.HasKey("Id");
45 |
46 | b.ToTable("Chats");
47 | });
48 |
49 | modelBuilder.Entity("ChatAppServer.WebAPI.Models.User", b =>
50 | {
51 | b.Property("Id")
52 | .ValueGeneratedOnAdd()
53 | .HasColumnType("uniqueidentifier");
54 |
55 | b.Property("Avatar")
56 | .IsRequired()
57 | .HasColumnType("nvarchar(max)");
58 |
59 | b.Property("Name")
60 | .IsRequired()
61 | .HasColumnType("nvarchar(max)");
62 |
63 | b.Property("Status")
64 | .IsRequired()
65 | .HasColumnType("nvarchar(max)");
66 |
67 | b.HasKey("Id");
68 |
69 | b.ToTable("Users");
70 | });
71 | #pragma warning restore 612, 618
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Models/Chat.cs:
--------------------------------------------------------------------------------
1 | namespace ChatAppServer.WebAPI.Models;
2 |
3 | public sealed class Chat
4 | {
5 | public Chat()
6 | {
7 | Id = Guid.NewGuid();
8 | }
9 | public Guid Id { get; set; }
10 | public Guid UserId { get; set; }
11 | public Guid ToUserId { get; set; }
12 | public string Message { get; set; } = string.Empty;
13 | public DateTime Date { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace ChatAppServer.WebAPI.Models;
2 |
3 | public sealed class User
4 | {
5 | public User()
6 | {
7 | Id = Guid.NewGuid();
8 | }
9 | public Guid Id { get; set; }
10 | public string Name { get; set; } = string.Empty;
11 | public string Avatar { get; set; } = string.Empty;
12 | public string Status { get; set; } = string.Empty;
13 | }
14 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/Program.cs:
--------------------------------------------------------------------------------
1 | using ChatAppServer.WebAPI.Context;
2 | using ChatAppServer.WebAPI.Hubs;
3 | using DefaultCorsPolicyNugetPackage;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | var builder = WebApplication.CreateBuilder(args);
7 |
8 | builder.Services.AddDefaultCors();
9 |
10 | builder.Services.AddDbContext(options => options.UseSqlServer(
11 | builder.Configuration.GetConnectionString("SqlServer")));
12 | builder.Services.AddControllers();
13 | builder.Services.AddEndpointsApiExplorer();
14 | builder.Services.AddSwaggerGen();
15 |
16 | builder.Services.AddSignalR();
17 |
18 | var app = builder.Build();
19 |
20 | if (app.Environment.IsDevelopment())
21 | {
22 | app.UseSwagger();
23 | app.UseSwaggerUI();
24 | }
25 |
26 | app.UseStaticFiles();
27 |
28 | app.UseHttpsRedirection();
29 |
30 | app.UseCors();
31 |
32 | app.UseAuthorization();
33 |
34 | app.MapControllers();
35 |
36 | app.MapHub("/chat-hub");
37 | app.Run();
38 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*",
9 | "ConnectionStrings": {
10 | "SqlServer": "Data Source=TANER\\SQLEXPRESS;Initial Catalog=ChatAppDb;Integrated Security=True;Connect Timeout=30;Encrypt=True;Trust Server Certificate=True;Application Intent=ReadWrite;Multi Subnet Failover=False"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/03cf04de-6ecb-4e43-b0b1-f828c805c71b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/03cf04de-6ecb-4e43-b0b1-f828c805c71b.png
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/17beaeac-4323-4f77-90e6-15a3e7711a15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/17beaeac-4323-4f77-90e6-15a3e7711a15.png
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/ca04e629-ebbf-4f81-acd8-1616864954a0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/ca04e629-ebbf-4f81-acd8-1616864954a0.png
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/e1eb1e28-0987-4c22-877a-39cb86f955bc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TanerSaydam/NET_Angular_SignalR_ile_ChatApp/855dac30d2f60f3a3116166c2ed29ad43f21081c/ChatAppServer/ChatAppServer.WebAPI/wwwroot/avatar/e1eb1e28-0987-4c22-877a-39cb86f955bc.png
--------------------------------------------------------------------------------
/ChatAppServer/ChatAppServer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.9.34728.123
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatAppServer.WebAPI", "ChatAppServer.WebAPI\ChatAppServer.WebAPI.csproj", "{33DA9B9A-E4D0-4E05-9E44-12960192A42F}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {33DA9B9A-E4D0-4E05-9E44-12960192A42F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {33DA9B9A-E4D0-4E05-9E44-12960192A42F}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {33DA9B9A-E4D0-4E05-9E44-12960192A42F}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {33DA9B9A-E4D0-4E05-9E44-12960192A42F}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {C0F66CAB-DCE9-4836-AD34-97CD1997AD2E}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # .NET, Angular ve SignalR ile Chat Uygulaması Eğitimi
2 | Arkadaşlar merhaba!
3 |
4 | Bu eğitim ile SignalR kullanarak canlı chat yapmak isteyenlerin örnek alabileceği bir proje hazırladım.
5 |
6 | Eğitim başında kopyala yapıştır ile aldığım dizayn kodları aşağıdadır.
7 |
8 | Repoyu yıldızlarayak destek vermeyi unutmayın
9 |
10 | - **Youtube videosu**
11 |
12 |
13 | İyi eğitimler.
14 |
15 |
16 | - **HTML**
17 | ```html
18 |
19 |
20 |
21 |
TS ChatAPP
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | @for(user of users; track user){
32 | -
33 |
34 |
35 |
{{user.name}}
36 |
{{user.status}}
37 |
38 |
39 | }
40 |
41 |
42 | @if(selectedUserId){
43 |
44 |
57 |
58 |
59 | @for(chat of chats; track chat){
60 | @if(selectedUserId != chat.userId){
61 | -
62 |
63 | {{chat.date}}
64 |
65 | {{chat.message}}
66 |
67 | }@else {
68 | -
69 |
70 | {{chat.date}}
71 |
72 | {{chat.message}}
73 |
74 | }
75 | }
76 |
77 |
78 |
86 |
87 | }@else {
88 |
89 |
102 |
103 |
104 | -
105 |
106 |
10:10 AM, Today
107 |

108 |
109 | Hi Aiden, how are you? How is the project coming along?
110 |
111 | -
112 |
113 | 10:12 AM, Today
114 |
115 | Are we meeting today?
116 |
117 | -
118 |
119 | 10:15 AM, Today
120 |
121 | Project has been already finished and I have results to show you.
122 |
123 |
124 |
125 |
133 |
134 | }
135 |
136 |
137 |
138 |
139 |
140 | ```
141 |
142 | - **TS**
143 | ```ts
144 | import { CommonModule } from '@angular/common';
145 | import { Component } from '@angular/core';
146 | import { RouterOutlet } from '@angular/router';
147 |
148 | @Component({
149 | selector: 'app-root',
150 | standalone: true,
151 | imports: [RouterOutlet, CommonModule],
152 | templateUrl: './app.component.html',
153 | styleUrl: './app.component.css'
154 | })
155 | export class AppComponent {
156 | users = Users;
157 | chats = Chats;
158 | selectedUserId: string = "1";
159 | selectedUser: UserModel = {
160 | id: "1",
161 | name: "Vincent Porter",
162 | status: "left 7 min ago",
163 | avatar: "avatar1.png"
164 | };
165 |
166 |
167 | changeUser(user: UserModel){
168 | this.selectedUserId = user.id;
169 | this.selectedUser = user;
170 |
171 | this.chats = Chats.filter(p=> p.toUserId == user.id && p.userId == "0" || p.userId == user.id && p.toUserId == "0");
172 | }
173 |
174 | }
175 |
176 | export class UserModel{
177 | id:string = "";
178 | name: string = "";
179 | status: string = "";
180 | avatar: string = "";
181 | }
182 |
183 | export const Users: UserModel[] = [
184 | {
185 | id: "1",
186 | name: "Vincent Porter",
187 | status: "left 7 min ago",
188 | avatar: "avatar1.png"
189 | },
190 | {
191 | id: "2",
192 | name: "Aiden Chavez",
193 | status: "online",
194 | avatar: "avatar3.png"
195 | },
196 | {
197 | id: "3",
198 | name: "Christian Kelly",
199 | status: "offline since oct 28",
200 | avatar: "avatar3.png"
201 | }
202 | ]
203 |
204 | export class ChatModel{
205 | userId: string = "";
206 | toUserId: string = "";
207 | date: string ="";
208 | message: string = "";
209 | }
210 |
211 | export const Chats: ChatModel[] = [
212 | {
213 | userId: "0",
214 | toUserId: "1",
215 | date: new Date().toString(),
216 | message: "Hi Aiden, how are you? How is the project coming along?"
217 | },
218 | {
219 | userId: "1",
220 | toUserId: "0",
221 | date: new Date().toString(),
222 | message: "Are we meeting today?"
223 | },
224 | {
225 | userId: "1",
226 | toUserId: "0",
227 | date: new Date().toString(),
228 | message: "Project has been already finished and I have results to show you."
229 | }
230 | ]
231 | ```
232 |
233 | - **CSS**
234 | ```css
235 | body{
236 | background-color: #f4f7f6;
237 | margin-top:20px;
238 | }
239 | .card {
240 | background: #fff;
241 | transition: .5s;
242 | border: 0;
243 | margin-bottom: 30px;
244 | border-radius: .55rem;
245 | position: relative;
246 | width: 100%;
247 | box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
248 | }
249 | .chat-app .people-list {
250 | width: 280px;
251 | position: absolute;
252 | left: 0;
253 | top: 0;
254 | padding: 20px;
255 | z-index: 7
256 | }
257 |
258 | .chat-app .chat {
259 | margin-left: 280px;
260 | border-left: 1px solid #eaeaea
261 | }
262 |
263 | .people-list {
264 | -moz-transition: .5s;
265 | -o-transition: .5s;
266 | -webkit-transition: .5s;
267 | transition: .5s
268 | }
269 |
270 | .people-list .chat-list li {
271 | padding: 10px 15px;
272 | list-style: none;
273 | border-radius: 3px
274 | }
275 |
276 | .people-list .chat-list li:hover {
277 | background: #efefef;
278 | cursor: pointer
279 | }
280 |
281 | .people-list .chat-list li.active {
282 | background: #efefef
283 | }
284 |
285 | .people-list .chat-list li .name {
286 | font-size: 15px
287 | }
288 |
289 | .people-list .chat-list img {
290 | width: 45px;
291 | border-radius: 50%
292 | }
293 |
294 | .people-list img {
295 | float: left;
296 | border-radius: 50%
297 | }
298 |
299 | .people-list .about {
300 | float: left;
301 | padding-left: 8px
302 | }
303 |
304 | .people-list .status {
305 | color: #999;
306 | font-size: 13px
307 | }
308 |
309 | .chat .chat-header {
310 | padding: 15px 20px;
311 | border-bottom: 2px solid #f4f7f6
312 | }
313 |
314 | .chat .chat-header img {
315 | float: left;
316 | border-radius: 40px;
317 | width: 40px
318 | }
319 |
320 | .chat .chat-header .chat-about {
321 | float: left;
322 | padding-left: 10px
323 | }
324 |
325 | .chat .chat-history {
326 | padding: 20px;
327 | border-bottom: 2px solid #fff
328 | }
329 |
330 | .chat .chat-history ul {
331 | padding: 0
332 | }
333 |
334 | .chat .chat-history ul li {
335 | list-style: none;
336 | margin-bottom: 30px
337 | }
338 |
339 | .chat .chat-history ul li:last-child {
340 | margin-bottom: 0px
341 | }
342 |
343 | .chat .chat-history .message-data {
344 | margin-bottom: 15px
345 | }
346 |
347 | .chat .chat-history .message-data img {
348 | border-radius: 40px;
349 | width: 40px
350 | }
351 |
352 | .chat .chat-history .message-data-time {
353 | color: #434651;
354 | padding-left: 6px
355 | }
356 |
357 | .chat .chat-history .message {
358 | color: #444;
359 | padding: 18px 20px;
360 | line-height: 26px;
361 | font-size: 16px;
362 | border-radius: 7px;
363 | display: inline-block;
364 | position: relative
365 | }
366 |
367 | .chat .chat-history .message:after {
368 | bottom: 100%;
369 | left: 7%;
370 | border: solid transparent;
371 | content: " ";
372 | height: 0;
373 | width: 0;
374 | position: absolute;
375 | pointer-events: none;
376 | border-bottom-color: #fff;
377 | border-width: 10px;
378 | margin-left: -10px
379 | }
380 |
381 | .chat .chat-history .my-message {
382 | background: #efefef
383 | }
384 |
385 | .chat .chat-history .my-message:after {
386 | bottom: 100%;
387 | left: 30px;
388 | border: solid transparent;
389 | content: " ";
390 | height: 0;
391 | width: 0;
392 | position: absolute;
393 | pointer-events: none;
394 | border-bottom-color: #efefef;
395 | border-width: 10px;
396 | margin-left: -10px
397 | }
398 |
399 | .chat .chat-history .other-message {
400 | background: #e8f1f3;
401 | text-align: right
402 | }
403 |
404 | .chat .chat-history .other-message:after {
405 | border-bottom-color: #e8f1f3;
406 | left: 93%
407 | }
408 |
409 | .chat .chat-message {
410 | padding: 20px
411 | }
412 |
413 | .online,
414 | .offline,
415 | .me {
416 | margin-right: 2px;
417 | font-size: 8px;
418 | vertical-align: middle
419 | }
420 |
421 | .online {
422 | color: #86c541
423 | }
424 |
425 | .offline {
426 | color: #e47297
427 | }
428 |
429 | .me {
430 | color: #1d8ecd
431 | }
432 |
433 | .float-right {
434 | float: right
435 | }
436 |
437 | .clearfix:after {
438 | visibility: hidden;
439 | display: block;
440 | font-size: 0;
441 | content: " ";
442 | clear: both;
443 | height: 0
444 | }
445 |
446 | @media only screen and (max-width: 767px) {
447 | .chat-app .people-list {
448 | height: 465px;
449 | width: 100%;
450 | overflow-x: auto;
451 | background: #fff;
452 | left: -400px;
453 | display: none
454 | }
455 | .chat-app .people-list.open {
456 | left: 0
457 | }
458 | .chat-app .chat {
459 | margin: 0
460 | }
461 | .chat-app .chat .chat-header {
462 | border-radius: 0.55rem 0.55rem 0 0
463 | }
464 | .chat-app .chat-history {
465 | height: 300px;
466 | overflow-x: auto
467 | }
468 | }
469 |
470 | @media only screen and (min-width: 768px) and (max-width: 992px) {
471 | .chat-app .chat-list {
472 | height: 650px;
473 | overflow-x: auto
474 | }
475 | .chat-app .chat-history {
476 | height: 600px;
477 | overflow-x: auto
478 | }
479 | }
480 |
481 | @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 1) {
482 | .chat-app .chat-list {
483 | height: 480px;
484 | overflow-x: auto
485 | }
486 | .chat-app .chat-history {
487 | height: calc(100vh - 350px);
488 | overflow-x: auto
489 | }
490 | }
491 | ```
492 |
493 | - **index.HTML**
494 | ```html
495 |
496 |
497 |
498 |
499 | ChatAppClient
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 | ```
--------------------------------------------------------------------------------