瀏覽代碼

version bump and frontend fixes

master
peter 5 年之前
父節點
當前提交
c888bf4514
共有 49 個文件被更改,包括 686 次插入685 次删除
  1. 33
    0
      log.txt
  2. 1
    1
      package.json
  3. 1
    3
      src/backend/Admin/Admin.ts
  4. 12
    2
      src/backend/Components/Raid/RaidManager.ts
  5. 0
    13
      src/backend/Components/Shoutbox/Interface.ts
  6. 0
    13
      src/backend/Components/Shoutbox/RPCInterface.ts
  7. 0
    64
      src/backend/Components/Shoutbox/Shoutbox.ts
  8. 1
    0
      src/backend/Components/User/UserManager.ts
  9. 1
    2
      src/backend/Types/Types.ts
  10. 89
    45
      src/frontend/package-lock.json
  11. 2
    0
      src/frontend/package.json
  12. 10
    3
      src/frontend/server.ts
  13. 0
    46
      src/frontend/src/app/@theme/components/header/chat.component.ts
  14. 0
    13
      src/frontend/src/app/@theme/components/header/header.component.html
  15. 1
    40
      src/frontend/src/app/@theme/components/header/header.component.ts
  16. 0
    3
      src/frontend/src/app/@theme/theme.module.ts
  17. 10
    3
      src/frontend/src/app/app.component.ts
  18. 2
    1
      src/frontend/src/app/app.module.ts
  19. 25
    7
      src/frontend/src/app/frontcraft/UpdatingComponent.ts
  20. 1
    1
      src/frontend/src/app/frontcraft/auth/login/login.component.ts
  21. 1
    1
      src/frontend/src/app/frontcraft/auth/logout/logout.component.ts
  22. 1
    1
      src/frontend/src/app/frontcraft/auth/register/register.component.ts
  23. 1
    1
      src/frontend/src/app/frontcraft/pages/armory/armory.component.ts
  24. 1
    1
      src/frontend/src/app/frontcraft/pages/character/character.component.ts
  25. 1
    1
      src/frontend/src/app/frontcraft/pages/characters/characters.component.ts
  26. 1
    1
      src/frontend/src/app/frontcraft/pages/pages-layout.component.ts
  27. 29
    20
      src/frontend/src/app/frontcraft/pages/raid/archive.component.ts
  28. 1
    1
      src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts
  29. 46
    48
      src/frontend/src/app/frontcraft/pages/raid/raid.component.html
  30. 193
    181
      src/frontend/src/app/frontcraft/pages/raid/raid.component.ts
  31. 3
    4
      src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts
  32. 38
    32
      src/frontend/src/app/frontcraft/pages/raids/raids.component.html
  33. 15
    12
      src/frontend/src/app/frontcraft/pages/raids/raids.component.ts
  34. 1
    1
      src/frontend/src/app/frontcraft/pages/rules/rules.component.ts
  35. 1
    1
      src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts
  36. 2
    2
      src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts
  37. 1
    1
      src/frontend/src/app/frontcraft/pages/shop/wowhead.component.ts
  38. 1
    1
      src/frontend/src/app/frontcraft/pages/user/user.component.ts
  39. 1
    1
      src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts
  40. 2
    3
      src/frontend/src/app/frontcraft/services/ApiService/ApiService.ts
  41. 4
    10
      src/frontend/src/app/frontcraft/services/ApiService/client-login-api.ts
  42. 34
    31
      src/frontend/src/app/frontcraft/services/ApiService/server-login-api.ts
  43. 10
    0
      src/frontend/src/app/frontcraft/services/LoggerService/LoggerService.ts
  44. 30
    0
      src/frontend/src/app/frontcraft/services/LoggerService/logger.service.ts
  45. 21
    0
      src/frontend/src/app/frontcraft/services/flash-service.ts
  46. 0
    37
      src/frontend/src/app/frontcraft/services/logger.service.ts
  47. 52
    27
      src/frontend/src/index.html
  48. 3
    3
      src/frontend/src/main.server.ts
  49. 3
    3
      src/frontend/src/main.ts

+ 33
- 0
log.txt 查看文件

@@ -0,0 +1,33 @@
1
+[2020-03-29T12:12:03.477] [DEBUG] Admin#makeKnex - Making new knex: {
2
+  client: 'sqlite3',
3
+  connection: {(node:1085628) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
4
+Terminated
5
+npm ERR! code ELIFECYCLE
6
+npm ERR! errno 143
7
+npm ERR! frontcraft@1.0.0 launch: `node lib/src/backend/Launcher.js > log.txt "-"`
8
+npm ERR! Exit status 143
9
+npm ERR! 
10
+npm ERR! Failed at the frontcraft@1.0.0 launch script.
11
+npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
12
+
13
+npm ERR! A complete log of this run can be found in:
14
+npm ERR!     /home/cake/.npm/_logs/2020-03-29T10_12_28_351Z-debug.log
15
+s loaded
16
+[2020-03-29T12:12:04.535] [INFO] Admin#startWebserver - Admin panel listening for HTTP on *8080
17
+[2020-03-29T12:12:17.689] [DEBUG] UserManager#checkConnection - {
18
+  celinda: {
19
+    connections: { '59288': [Socket] },
20
+    auth: { token: [Object], user: [Object], port: 20001 },
21
+    user: {
22
+      id: 3,
23
+      username: 'celinda',
24
+      rank: 'Officer',
25
+      MC: 1,
26
+      BWL: 0,
27
+      ZG: 1,
28
+      AQ20: 1,
29
+      AQ40: 1,
30
+      Naxx: 1
31
+    }
32
+  }
33
+} [ '1563eebe-9da2-4b31-b4ef-ef3aaa7e23b1' ]

+ 1
- 1
package.json 查看文件

@@ -5,7 +5,7 @@
5 5
   "scripts": {
6 6
     "tsc": "tsc",
7 7
     "knex": "knex",
8
-    "launch": "node lib/src/backend/Launcher.js",
8
+    "launch": "node lib/src/backend/Launcher.js > log.txt",
9 9
     "start": "npm run build && npm run launch",
10 10
     "start-backend": "npm run build-backend && npm run launch",
11 11
     "test": "rm -rf data && npm run build-backend && mocha lib/test/backendTest.js",

+ 1
- 3
src/backend/Admin/Admin.ts 查看文件

@@ -19,7 +19,6 @@ import { RPCConfigLoader } from '../Components/RPCConfigLoader';
19 19
 import { FrontworkComponent } from '../Types/FrontworkComponent';
20 20
 import { IAdmin } from './Interface';
21 21
 import { Injector } from '../Injector/Injector';
22
-import { Shoutbox } from '../Components/Shoutbox/Shoutbox';
23 22
 import { PubSub } from '../Components/PubSub/PubSub';
24 23
 
25 24
 getLogger().level = 'debug'
@@ -32,7 +31,6 @@ getLogger().level = 'debug'
32 31
         RaidManager,
33 32
         CharacterManager,
34 33
         UserManager,
35
-        Shoutbox,
36 34
         PubSub
37 35
     ]
38 36
 })
@@ -145,7 +143,7 @@ export class FrontworkAdmin
145 143
 
146 144
         try {
147 145
             const req = require(distFolder + "/server.js")
148
-            await req.attachExpress(this.express)
146
+            await req.attachExpress(this.express, './dist', getLogger('angularSSR#'))
149 147
             getLogger('Admin#startWebserver').info('Frontend from ' + ngExpressServer + " loaded")
150 148
         } catch (e) {
151 149
             getLogger('Admin#startWebserver').error(e)

+ 12
- 2
src/backend/Components/Raid/RaidManager.ts 查看文件

@@ -145,7 +145,7 @@ export class RaidManager
145 145
 
146 146
     getRaids = async (): Promise<Raid[]> => {
147 147
 
148
-        const subQuery = this.admin
148
+        const countSignups = this.admin
149 149
             .knex('signups')
150 150
             .count('*')
151 151
             .where({
@@ -155,8 +155,18 @@ export class RaidManager
155 155
             })
156 156
             .as('signupcount')
157 157
 
158
+        const countBenches = this.admin
159
+            .knex('signups')
160
+            .count('*')
161
+            .where({
162
+                raidid: this.admin.knex.ref('raids.id'),
163
+                benched: true,
164
+                late: false
165
+            })
166
+            .as('benchcount')
167
+
158 168
         return await this.admin.knex('raids')
159
-            .select('*', subQuery)
169
+            .select('*', countSignups, countBenches)
160 170
             .orderBy('start', 'asc')
161 171
     }
162 172
 

+ 0
- 13
src/backend/Components/Shoutbox/Interface.ts 查看文件

@@ -1,13 +0,0 @@
1
-import { Raid, Signup, Character, RaidData } from "../../Types/Types"
2
-
3
-export type ShoutMessage = {
4
-    message: string,
5
-    sender: string,
6
-    date: string
7
-}
8
-
9
-export class IShoutbox{
10
-    shout: (uuid:string, msg: ShoutMessage) => Promise<void>
11
-    getFeed: () => Promise<ShoutMessage[]>
12
-    subscribe: (callback) => Promise<string>
13
-}

+ 0
- 13
src/backend/Components/Shoutbox/RPCInterface.ts 查看文件

@@ -1,13 +0,0 @@
1
-import { IShoutbox } from "./Interface"
2
-
3
-export type ShoutboxIfc = {
4
-    Shoutbox:{
5
-        getFeed: IShoutbox['getFeed']
6
-        shout: IShoutbox['shout']
7
-        subscribe: IShoutbox['subscribe']
8
-    }
9
-}
10
-
11
-export type ShoutboxFeatureIfc = {
12
-    
13
-}

+ 0
- 64
src/backend/Components/Shoutbox/Shoutbox.ts 查看文件

@@ -1,64 +0,0 @@
1
-import { Injectable } from "../../Injector/ServiceDecorator";
2
-import { ShoutboxFeatureIfc, ShoutboxIfc } from "./RPCInterface";
3
-import { FrontworkComponent } from "../../Types/FrontworkComponent";
4
-import { TableDefiniton } from "../../Types/Types";
5
-import { IShoutbox, ShoutMessage } from "./Interface";
6
-import * as  CircularBuffer from "circular-buffer";
7
-const uuid = require('uuid/v4')
8
-
9
-@Injectable(IShoutbox)
10
-export class Shoutbox
11
-implements FrontworkComponent<ShoutboxIfc, ShoutboxFeatureIfc>, IShoutbox{
12
-    name = "Shoutbox" as "Shoutbox";    
13
-
14
-    log: CircularBuffer = new CircularBuffer(500)
15
-    subs = {}
16
-
17
-    exportRPCs = () => [
18
-        this.shout,
19
-        this.getFeed,
20
-        {
21
-            name: 'subscribe' as 'subscribe',
22
-            hook: this.subscribe,
23
-            onClose: (subres, rpcName) => {
24
-                this.unsubscribe(subres.uuid)
25
-            }
26
-        }
27
-    ]
28
-
29
-    exportRPCFeatures = () => []
30
-    
31
-    getTableDefinitions(): TableDefiniton[] {
32
-        return []
33
-    }
34
-
35
-    shout = async (uuid:string, msg: ShoutMessage) : Promise<void> => {
36
-        if(!this.subs[uuid]) return
37
-        this.broadcast(uuid, msg)
38
-        this.log.push(msg)
39
-    }
40
-
41
-    private broadcast = async (uuid:string, msg: ShoutMessage) => {
42
-        await Promise.all(Object.values(this.subs).map(async (callback:any) => {
43
-            try{
44
-                await callback(msg)
45
-            }catch(e){
46
-                delete this.subs[uuid]
47
-            }
48
-        }))
49
-    }
50
-
51
-    getFeed = async () : Promise<ShoutMessage[]> => {
52
-        return this.log.toarray()
53
-    }
54
-
55
-    subscribe = async (callback) : Promise<string> => {
56
-        uuid()
57
-        this.subs[uuid] = callback
58
-        return uuid
59
-    }
60
-
61
-    unsubscribe = async (uuid: string) => {
62
-        delete this.subs[uuid]
63
-    }
64
-}

+ 1
- 0
src/backend/Components/User/UserManager.ts 查看文件

@@ -404,6 +404,7 @@ export class UserManager
404 404
             },
405 405
             errorHandler: (socket, e, rpcName, args) => {
406 406
                 getLogger('UserManager#errorHandler').error(rpcName, args, e);
407
+                throw(new Error("RPC failed"))
407 408
             },
408 409
             sesame: (sesame) => this.checkToken(sesame),
409 410
             visibility: '0.0.0.0'

+ 1
- 2
src/backend/Types/Types.ts 查看文件

@@ -5,7 +5,6 @@ import { UserManagerIfc, UserManagerFeatureIfc } from "../Components/User/RPCInt
5 5
 import { CharacterManagerIfc, CharacterManagerFeatureIfc } from "../Components/Character/RPCInterface";
6 6
 import { ItemManagerFeatureIfc, ItemManagerIfc } from "../Components/Item/RPCInterface";
7 7
 import { GuildManagerFeatureIfc, GuildManagerIfc } from "../Components/Guild/RPCInterface";
8
-import { ShoutboxIfc } from "../Components/Shoutbox/RPCInterface";
9 8
 import { Tiers } from "./Items";
10 9
 import { PubSubIfc } from "../Components/PubSub/RPCInterface";
11 10
 
@@ -14,7 +13,6 @@ export type FrontcraftIfc = RaidManagerIfc
14 13
                           & CharacterManagerIfc 
15 14
                           & ItemManagerIfc
16 15
                           & GuildManagerIfc
17
-                          & ShoutboxIfc
18 16
                           & PubSubIfc
19 17
 
20 18
 export type FrontcraftFeatureIfc = RaidManagerFeatureIfc 
@@ -155,6 +153,7 @@ export type Raid = {
155 153
     description: string
156 154
     start: string
157 155
     signupcount?: number
156
+    benchcount?: number
158 157
     size: number
159 158
     tier: Tiers
160 159
 }

+ 89
- 45
src/frontend/package-lock.json 查看文件

@@ -6288,6 +6288,11 @@
6288 6288
         "assert-plus": "^1.0.0"
6289 6289
       }
6290 6290
     },
6291
+    "date-format": {
6292
+      "version": "3.0.0",
6293
+      "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
6294
+      "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w=="
6295
+    },
6291 6296
     "dateformat": {
6292 6297
       "version": "3.0.3",
6293 6298
       "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@@ -6298,7 +6303,6 @@
6298 6303
       "version": "4.1.1",
6299 6304
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
6300 6305
       "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
6301
-      "dev": true,
6302 6306
       "requires": {
6303 6307
         "ms": "^2.1.1"
6304 6308
       }
@@ -8095,6 +8099,11 @@
8095 8099
         }
8096 8100
       }
8097 8101
     },
8102
+    "flatted": {
8103
+      "version": "2.0.1",
8104
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
8105
+      "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg=="
8106
+    },
8098 8107
     "flatten": {
8099 8108
       "version": "1.0.3",
8100 8109
       "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
@@ -8131,6 +8140,11 @@
8131 8140
         }
8132 8141
       }
8133 8142
     },
8143
+    "font-awesome": {
8144
+      "version": "4.7.0",
8145
+      "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
8146
+      "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
8147
+    },
8134 8148
     "fontkit": {
8135 8149
       "version": "1.8.0",
8136 8150
       "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.8.0.tgz",
@@ -8324,7 +8338,6 @@
8324 8338
       "version": "8.1.0",
8325 8339
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
8326 8340
       "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
8327
-      "dev": true,
8328 8341
       "requires": {
8329 8342
         "graceful-fs": "^4.2.0",
8330 8343
         "jsonfile": "^4.0.0",
@@ -10435,7 +10448,6 @@
10435 10448
       "version": "4.0.0",
10436 10449
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
10437 10450
       "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
10438
-      "dev": true,
10439 10451
       "requires": {
10440 10452
         "graceful-fs": "^4.1.6"
10441 10453
       }
@@ -11327,6 +11339,12 @@
11327 11339
             "kind-of": "^3.0.2"
11328 11340
           }
11329 11341
         },
11342
+        "isarray": {
11343
+          "version": "0.0.1",
11344
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
11345
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
11346
+          "dev": true
11347
+        },
11330 11348
         "kind-of": {
11331 11349
           "version": "3.2.2",
11332 11350
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -11342,6 +11360,30 @@
11342 11360
           "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
11343 11361
           "dev": true
11344 11362
         },
11363
+        "log4js": {
11364
+          "version": "0.6.38",
11365
+          "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz",
11366
+          "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=",
11367
+          "dev": true,
11368
+          "requires": {
11369
+            "readable-stream": "~1.0.2",
11370
+            "semver": "~4.3.3"
11371
+          },
11372
+          "dependencies": {
11373
+            "readable-stream": {
11374
+              "version": "1.0.34",
11375
+              "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
11376
+              "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
11377
+              "dev": true,
11378
+              "requires": {
11379
+                "core-util-is": "~1.0.0",
11380
+                "inherits": "~2.0.1",
11381
+                "isarray": "0.0.1",
11382
+                "string_decoder": "~0.10.x"
11383
+              }
11384
+            }
11385
+          }
11386
+        },
11345 11387
         "micromatch": {
11346 11388
           "version": "2.3.11",
11347 11389
           "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
@@ -11587,12 +11629,24 @@
11587 11629
             }
11588 11630
           }
11589 11631
         },
11632
+        "semver": {
11633
+          "version": "4.3.6",
11634
+          "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
11635
+          "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
11636
+          "dev": true
11637
+        },
11590 11638
         "source-map": {
11591 11639
           "version": "0.5.7",
11592 11640
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
11593 11641
           "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
11594 11642
           "dev": true
11595 11643
         },
11644
+        "string_decoder": {
11645
+          "version": "0.10.31",
11646
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
11647
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
11648
+          "dev": true
11649
+        },
11596 11650
         "tmp": {
11597 11651
           "version": "0.0.31",
11598 11652
           "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
@@ -12724,45 +12778,15 @@
12724 12778
       }
12725 12779
     },
12726 12780
     "log4js": {
12727
-      "version": "0.6.38",
12728
-      "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz",
12729
-      "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=",
12730
-      "dev": true,
12781
+      "version": "6.1.2",
12782
+      "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.1.2.tgz",
12783
+      "integrity": "sha512-knS4Y30pC1e0n7rfx3VxcLOdBCsEo0o6/C7PVTGxdVK+5b1TYOSGQPn9FDcrhkoQBV29qwmA2mtkznPAQKnxQg==",
12731 12784
       "requires": {
12732
-        "readable-stream": "~1.0.2",
12733
-        "semver": "~4.3.3"
12734
-      },
12735
-      "dependencies": {
12736
-        "isarray": {
12737
-          "version": "0.0.1",
12738
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
12739
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
12740
-          "dev": true
12741
-        },
12742
-        "readable-stream": {
12743
-          "version": "1.0.34",
12744
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
12745
-          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
12746
-          "dev": true,
12747
-          "requires": {
12748
-            "core-util-is": "~1.0.0",
12749
-            "inherits": "~2.0.1",
12750
-            "isarray": "0.0.1",
12751
-            "string_decoder": "~0.10.x"
12752
-          }
12753
-        },
12754
-        "semver": {
12755
-          "version": "4.3.6",
12756
-          "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
12757
-          "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=",
12758
-          "dev": true
12759
-        },
12760
-        "string_decoder": {
12761
-          "version": "0.10.31",
12762
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
12763
-          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
12764
-          "dev": true
12765
-        }
12785
+        "date-format": "^3.0.0",
12786
+        "debug": "^4.1.1",
12787
+        "flatted": "^2.0.1",
12788
+        "rfdc": "^1.1.4",
12789
+        "streamroller": "^2.2.3"
12766 12790
       }
12767 12791
     },
12768 12792
     "loglevel": {
@@ -13376,8 +13400,7 @@
13376 13400
     "ms": {
13377 13401
       "version": "2.1.2",
13378 13402
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
13379
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
13380
-      "dev": true
13403
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
13381 13404
     },
13382 13405
     "multicast-dns": {
13383 13406
       "version": "6.2.3",
@@ -15717,6 +15740,11 @@
15717 15740
       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
15718 15741
       "dev": true
15719 15742
     },
15743
+    "rfdc": {
15744
+      "version": "1.1.4",
15745
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
15746
+      "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
15747
+    },
15720 15748
     "rimraf": {
15721 15749
       "version": "2.6.1",
15722 15750
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
@@ -17025,6 +17053,23 @@
17025 17053
       "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
17026 17054
       "dev": true
17027 17055
     },
17056
+    "streamroller": {
17057
+      "version": "2.2.3",
17058
+      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.3.tgz",
17059
+      "integrity": "sha512-AegmvQsscTRhHVO46PhCDerjIpxi7E+d2GxgUDu+nzw/HuLnUdxHWr6WQ+mVn/4iJgMKKFFdiUwFcFRDvcjCtw==",
17060
+      "requires": {
17061
+        "date-format": "^2.1.0",
17062
+        "debug": "^4.1.1",
17063
+        "fs-extra": "^8.1.0"
17064
+      },
17065
+      "dependencies": {
17066
+        "date-format": {
17067
+          "version": "2.1.0",
17068
+          "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
17069
+          "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA=="
17070
+        }
17071
+      }
17072
+    },
17028 17073
     "strict-uri-encode": {
17029 17074
       "version": "1.1.0",
17030 17075
       "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
@@ -18508,8 +18553,7 @@
18508 18553
     "universalify": {
18509 18554
       "version": "0.1.2",
18510 18555
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
18511
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
18512
-      "dev": true
18556
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
18513 18557
     },
18514 18558
     "unix-crypt-td-js": {
18515 18559
       "version": "1.1.4",

+ 2
- 0
src/frontend/package.json 查看文件

@@ -62,10 +62,12 @@
62 62
     "core-js": "2.5.1",
63 63
     "echarts": "^4.0.2",
64 64
     "eva-icons": "^1.1.0",
65
+    "font-awesome": "^4.7.0",
65 66
     "intl": "1.2.5",
66 67
     "ionicons": "2.0.1",
67 68
     "js-cookie": "^2.2.1",
68 69
     "leaflet": "1.2.0",
70
+    "log4js": "^6.1.2",
69 71
     "nebular-icons": "1.1.0",
70 72
     "ng2-ckeditor": "^1.2.2",
71 73
     "ng2-completer": "2.0.8",

+ 10
- 3
src/frontend/server.ts 查看文件

@@ -13,15 +13,22 @@ global['document'] = win.document;
13 13
 global['alert'] = console.log
14 14
 global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
15 15
 
16
-export async function attachExpress(app, staticDir = "./dist", loggerService = console) {
16
+export async function attachExpress(app, staticDir = "./dist", logger = console) {
17 17
   const STATIC_FOLDER = resolve(process.cwd(), staticDir);
18
-
19 18
   enableProdMode();
20 19
 
20
+  const loggerService = {
21
+    warn: (...args) => logger.error(...args),
22
+    error: (...args) => logger.warn(...args),
23
+    log: (...args) => logger.info(...args),
24
+    table: (...args) => logger.info(...args),
25
+    collapsed: (msg, type, ...args) => loggerService[type](msg, ...args)
26
+  }
27
+
21 28
   const bundle = require(staticDir + '/server/main');
22 29
 
23 30
   const ServerApiService = bundle.ServerApiService
24
-  const serviceObj = new ServerApiService(loggerService)
31
+  const serviceObj = new ServerApiService(logger)
25 32
   await serviceObj.initialize()
26 33
 
27 34
   app.set('view engine', 'html');

+ 0
- 46
src/frontend/src/app/@theme/components/header/chat.component.ts 查看文件

@@ -1,46 +0,0 @@
1
-import { Component } from '@angular/core';
2
-import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface';
3
-import { IApiService } from '../../../frontcraft/services/ApiService';
4
-
5
-@Component({
6
-  selector: 'chat',
7
-  template: `
8
-        <nb-chat title="Shoutbox" size="large" status="primary">
9
-          <nb-chat-message *ngFor="let msg of messages"
10
-                           [type]="text"
11
-                           [message]="msg.message"
12
-                           [sender]="msg.sender"
13
-                           [date]="msg.date"
14
-                           [reply]="msg.reply">
15
-          </nb-chat-message>
16
-          <nb-chat-form 
17
-          (send)="submit($event)" 
18
-          [dropFiles]="false">
19
-          </nb-chat-form>
20
-        </nb-chat>
21
-  `, 
22
-  styles: [`
23
-  nb-chat {
24
-    width: 600px;
25
-    margin: 0.5rem 0 2rem 2rem;
26
-  }`],
27
-})
28
-export class ChatComponent {
29
-
30
-  messages : ShoutMessage[] = []
31
-  history
32
-
33
-  constructor(
34
-    private api: IApiService
35
-  ) {}
36
-
37
-  submit(event){
38
-    this.sendMessage({
39
-      date: ""+Date.now(),
40
-      message: event.message,
41
-      sender: this.api.getCurrentUser().username
42
-    })
43
-  }
44
-
45
-  sendMessage : (msg:ShoutMessage)=>Promise<void>
46
-}

+ 0
- 13
src/frontend/src/app/@theme/components/header/header.component.html 查看文件

@@ -14,19 +14,6 @@
14 14
       icon="settings-2-outline" 
15 15
       link="/permissions">
16 16
     </nb-action>
17
-    <nb-action 
18
-      *ngIf="!newmessage"
19
-      icon="message-square-outline" 
20
-      (click)="openChat()">
21
-    </nb-action>
22
-    <nb-action 
23
-      *ngIf="newmessage"
24
-      icon="message-square-outline"
25
-      badgeText="new"
26
-      badgePosition="top right"
27
-      badgeStatus="info"
28
-      (click)="openChat()">
29
-    </nb-action>
30 17
   
31 18
     <nb-action class="user-action">
32 19
       <nb-user style="text-transform: capitalize;"

+ 1
- 40
src/frontend/src/app/@theme/components/header/header.component.ts 查看文件

@@ -6,9 +6,7 @@ import { map, takeUntil } from 'rxjs/operators';
6 6
 import { Subject } from 'rxjs';
7 7
 import { User } from '../../../../../../backend/Types/Types';
8 8
 import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
9
-import { ChatComponent } from './chat.component';
10
-import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface';
11
-import { IApiService } from '../../../frontcraft/services/ApiService';
9
+import { IApiService } from '../../../frontcraft/services/ApiService/ApiService';
12 10
 
13 11
 @Component({
14 12
   selector: 'ngx-header',
@@ -44,8 +42,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
44 42
   userMenu: NbMenuItem[] = [];
45 43
 
46 44
   sendMessage: any = console.log
47
-  chatwindow: ChatComponent
48
-  chatlog: ShoutMessage[] = []
49 45
   newmessage = false
50 46
   lastmessage = "asdasd"
51 47
   modifyPermissions
@@ -56,8 +52,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
56 52
     private themeService: NbThemeService,
57 53
     private layoutService: LayoutService,
58 54
     private api: IApiService,
59
-    private dialogService: NbDialogService,
60
-    private route: ActivatedRoute
61 55
   ) { }
62 56
 
63 57
   private setUserMenu = (redirect = this.router.url) => {
@@ -89,29 +83,10 @@ export class HeaderComponent implements OnInit, OnDestroy {
89 83
       }
90 84
     })
91 85
 
92
-
93
-
94 86
     this.api.get('GuildManager').getGuildInfo().then(info => {
95 87
       this.title = info.name
96 88
     })
97 89
 
98
-    this.api.connectShoutbox((msg) => {
99
-      this.chatlog.push(msg)
100
-      msg['reply'] = false
101
-
102
-      if (msg.message != this.lastmessage) {
103
-        this.newmessage = true
104
-        msg['reply'] = true
105
-      }
106
-      if (this.chatwindow) this.chatwindow.messages.push(msg)
107
-    }).then(sendMsg => {
108
-      this.sendMessage = (msg) => {
109
-        sendMsg(msg)
110
-        this.lastmessage = msg.message
111
-      }
112
-      this.api.get('Shoutbox').getFeed().then(log => { this.chatlog = log })
113
-    })
114
-
115 90
     this.themeService.onThemeChange()
116 91
       .pipe(
117 92
         map(({ name }) => name),
@@ -120,20 +95,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
120 95
       .subscribe(themeName => this.currentTheme = themeName);
121 96
   }
122 97
 
123
-  openChat() {
124
-    this.newmessage = false
125
-    const ref = this.dialogService.open(ChatComponent, {
126
-      context: {
127
-        sendMessage: this.sendMessage,
128
-      }
129
-    });
130
-    ref.onClose.subscribe(() => {
131
-      this.chatwindow = null
132
-    })
133
-    this.chatwindow = ref.componentRef.instance
134
-    this.chatwindow.messages.push(...this.chatlog)
135
-  }
136
-
137 98
   ngOnDestroy() {
138 99
     this.destroy$.next();
139 100
     this.destroy$.complete();

+ 0
- 3
src/frontend/src/app/@theme/theme.module.ts 查看文件

@@ -42,7 +42,6 @@ import { DEFAULT_THEME } from './styles/theme.default';
42 42
 import { COSMIC_THEME } from './styles/theme.cosmic';
43 43
 import { CORPORATE_THEME } from './styles/theme.corporate';
44 44
 import { DARK_THEME } from './styles/theme.dark';
45
-import { ChatComponent } from './components/header/chat.component';
46 45
 
47 46
 const NB_MODULES = [
48 47
   NbLayoutModule,
@@ -70,7 +69,6 @@ const COMPONENTS = [
70 69
   ThreeColumnsLayoutComponent,
71 70
   TwoColumnsLayoutComponent,
72 71
   OneColumnNoSidebarLayoutComponent,
73
-  ChatComponent
74 72
 ];
75 73
 const PIPES = [
76 74
   CapitalizePipe,
@@ -85,7 +83,6 @@ const PIPES = [
85 83
   exports: [CommonModule, ...PIPES, ...COMPONENTS],
86 84
   declarations: [...COMPONENTS, ...PIPES],
87 85
   entryComponents: [
88
-    ChatComponent
89 86
   ]
90 87
 })
91 88
 export class ThemeModule {

+ 10
- 3
src/frontend/src/app/app.component.ts 查看文件

@@ -3,10 +3,17 @@
3 3
  * Copyright Akveo. All Rights Reserved.
4 4
  * Licensed under the MIT License. See License.txt in the project root for license information.
5 5
  */
6
-import { Component, OnInit } from '@angular/core';
6
+import { Component } from '@angular/core';
7
+import { NbIconLibraries } from '@nebular/theme';
8
+
7 9
 
8 10
 @Component({
9 11
   selector: 'ngx-app',
10
-  template: '<router-outlet></router-outlet>',
12
+  template: `<router-outlet></router-outlet>`,
13
+  styles: []
11 14
 })
12
-export class AppComponent { }
15
+export class AppComponent{
16
+  constructor(private iconLibraries: NbIconLibraries) {
17
+    this.iconLibraries.registerFontPack('font-awesome', { iconClassPrefix: 'fa' });
18
+  }
19
+}

+ 2
- 1
src/frontend/src/app/app.module.ts 查看文件

@@ -23,6 +23,7 @@ import {
23 23
   NbToastrModule,
24 24
   NbWindowModule,
25 25
 } from '@nebular/theme';
26
+import { FlashService } from './frontcraft/services/flash-service';
26 27
 
27 28
 @NgModule({
28 29
   declarations: [
@@ -49,7 +50,7 @@ import {
49 50
     PermissionsModule
50 51
   ],
51 52
   bootstrap: [AppComponent],
52
-  providers: []
53
+  providers: [FlashService]
53 54
 })
54 55
 export class FrontcraftAppModule {
55 56
 }

+ 25
- 7
src/frontend/src/app/frontcraft/UpdatingComponent.ts 查看文件

@@ -1,23 +1,41 @@
1 1
 import { Router, NavigationStart } from '@angular/router'
2
-import { IApiService } from './services/ApiService'
2
+import { IApiService } from './services/ApiService/ApiService'
3 3
 import { AnyFunction } from 'rpclibrary'
4
-import { LoggerService } from './services/logger.service'
5
-import { OnDestroy } from '@angular/core'
4
+import { LoggerService } from './services/LoggerService/logger.service'
5
+import { OnDestroy, Inject, PLATFORM_ID, Injector } from '@angular/core'
6
+import { isPlatformBrowser } from '@angular/common'
6 7
 
7 8
 export abstract class UpdatingComponent implements OnDestroy {
9
+
8 10
     private _uuid: string
9 11
     private _routerCancel: Function
12
+    private _router: Router
13
+    private _api: IApiService
14
+    private _logger: LoggerService
15
+    private platformId: Object
10 16
 
11 17
     constructor(
12
-        private _router: Router,
13
-        private _api: IApiService,
14
-        private _logger: LoggerService
15
-    ) { }
18
+        injector: Injector
19
+    ) {
20
+        this._router = injector.get(Router)
21
+        this._api = injector.get(IApiService)
22
+        this._logger = injector.get(LoggerService)
23
+        this.platformId = injector.get(PLATFORM_ID)
24
+    }
16 25
 
17 26
     protected subscribe = (
18 27
         topic: string,
19 28
         callback: AnyFunction,
20 29
     ) => {
30
+        if (!topic
31
+        || topic === "null"
32
+        || topic === "undefined"
33
+        || !callback
34
+        || !isPlatformBrowser(this.platformId)) 
35
+        {
36
+            return
37
+        }
38
+
21 39
         const sub = this._router.events.subscribe(event => {
22 40
             if (event instanceof NavigationStart) {
23 41
                 this.ngOnDestroy()

+ 1
- 1
src/frontend/src/app/frontcraft/auth/login/login.component.ts 查看文件

@@ -1,6 +1,6 @@
1 1
 import { Component, OnInit } from '@angular/core';
2 2
 import { Router, ActivatedRoute } from '@angular/router';
3
-import { IApiService } from '../../services/ApiService';
3
+import { IApiService } from '../../services/ApiService/ApiService';
4 4
 import { NbToastrService } from '@nebular/theme';
5 5
 
6 6
 @Component({

+ 1
- 1
src/frontend/src/app/frontcraft/auth/logout/logout.component.ts 查看文件

@@ -1,5 +1,5 @@
1 1
 import { Component, OnInit } from '@angular/core';
2
-import { IApiService } from '../../services/ApiService';
2
+import { IApiService } from '../../services/ApiService/ApiService';
3 3
 import { Router, ActivatedRoute } from '@angular/router';
4 4
 
5 5
 @Component({

+ 1
- 1
src/frontend/src/app/frontcraft/auth/register/register.component.ts 查看文件

@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
2 2
 import { Router, ActivatedRoute } from '@angular/router';
3 3
 import { _Rank, _Class, Class, User, _Race  } from '../../../../../../backend/Types/Types'
4 4
 import { specs  } from '../../../../../../backend/Types/PlayerSpecs'
5
-import { hash, IApiService } from '../../services/ApiService';
5
+import { hash, IApiService } from '../../services/ApiService/ApiService';
6 6
 
7 7
 
8 8
 @Component({

+ 1
- 1
src/frontend/src/app/frontcraft/pages/armory/armory.component.ts 查看文件

@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
2 2
 import { Item, Stats } from '../../../../../../backend/Types/Types';
3 3
 
4 4
 import { NbToastrService } from '@nebular/theme';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6 6
 
7 7
 @Component({
8 8
   selector: 'armory',

+ 1
- 1
src/frontend/src/app/frontcraft/pages/character/character.component.ts 查看文件

@@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
3 3
 import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types';
4 4
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5 5
 import { _Tiers } from '../../../../../../backend/Types/Items';
6
-import { IApiService } from '../../services/ApiService';
6
+import { IApiService } from '../../services/ApiService/ApiService';
7 7
 
8 8
 @Component({
9 9
   selector: 'character',

+ 1
- 1
src/frontend/src/app/frontcraft/pages/characters/characters.component.ts 查看文件

@@ -2,7 +2,7 @@ import { Component, AfterViewInit, OnDestroy } from '@angular/core';
2 2
 import { NbThemeService } from '@nebular/theme';
3 3
 import { _Class } from '../../../../../../backend/Types/Types';
4 4
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6 6
 
7 7
 
8 8
 @Component({

+ 1
- 1
src/frontend/src/app/frontcraft/pages/pages-layout.component.ts 查看文件

@@ -1,7 +1,7 @@
1 1
 import { Component, OnInit } from '@angular/core';
2 2
 import { NbMenuItem } from '@nebular/theme';
3 3
 import { Router } from '@angular/router';
4
-import { IApiService } from '../services/ApiService';
4
+import { IApiService } from '../services/ApiService/ApiService';
5 5
 
6 6
 
7 7
 @Component({

+ 29
- 20
src/frontend/src/app/frontcraft/pages/raid/archive.component.ts 查看文件

@@ -1,8 +1,9 @@
1 1
 import { Component, OnInit } from '@angular/core';
2
-import { ActivatedRoute } from '@angular/router';
2
+import { ActivatedRoute, Router } from '@angular/router';
3 3
 import { RaidData } from '../../../../../../backend/Types/Types';
4 4
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6
+import { LoggerService } from '../../services/LoggerService/logger.service';
6 7
 
7 8
 @Component({
8 9
   selector: 'archive',
@@ -40,6 +41,8 @@ export class FrontcraftArchiveComponent implements OnInit{
40 41
     constructor(
41 42
       private api: IApiService,
42 43
       private route: ActivatedRoute,
44
+      private router: Router,
45
+      private logger: LoggerService
43 46
     ){
44 47
     }
45 48
 
@@ -51,25 +54,31 @@ export class FrontcraftArchiveComponent implements OnInit{
51 54
       const param = this.route.snapshot.paramMap.get('id');
52 55
 
53 56
       const raidManager = this.api.get('RaidManager')
54
-      const raiddata = await raidManager.getArchiveRaid(parseInt(param))
57
+      try{
58
+        const raiddata = await raidManager.getArchiveRaid(parseInt(param))
59
+        if(!raiddata) throw new Error('Unable to get raid')
55 60
 
56
-      console.log(raiddata);
57
-      
58
-
59
-      this.raid = raiddata
60
-      this.isTier = raiddata.tier != null
61
-      this.tokens = raiddata.tokens
62
-      this.displayedtokens = this.tokens;
63
-
64
-      [
65
-        ...raiddata.tanks, 
66
-        ...raiddata.healers, 
67
-        ...Object.values<any>(raiddata.participants).flat()
68
-      ].forEach(p => {
69
-        p['color'] = getClassColor(p.class)
70
-      })
71
-
72
-      this.changeSearch()
61
+        this.raid = raiddata
62
+        this.isTier = raiddata.tier != null
63
+        this.tokens = raiddata.tokens
64
+        this.displayedtokens = this.tokens;
65
+  
66
+        [
67
+          ...raiddata.tanks, 
68
+          ...raiddata.healers, 
69
+          ...Object.values<any>(raiddata.participants).flat()
70
+        ].forEach(p => {
71
+          p['color'] = getClassColor(p.class)
72
+          if(!p.timestamp)
73
+            p.before = 5.184e+8 //7 days
74
+          else
75
+            p['before'] = Number.parseInt(this.raid.start) - Date.parse(p.timestamp)
76
+        })
77
+  
78
+        this.changeSearch()
79
+      }catch(e){
80
+        this.router.navigate(["/"])
81
+      }
73 82
     }
74 83
 
75 84
     changeSearch(){

+ 1
- 1
src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts 查看文件

@@ -2,7 +2,7 @@ import { OnInit, Component } from '@angular/core';
2 2
 import { NbToastrService, NbDialogRef } from '@nebular/theme';
3 3
 import { RaidData, Character } from '../../../../../../backend/Types/Types';
4 4
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6 6
 
7 7
 @Component({
8 8
     selector: 'characterpicker',

+ 46
- 48
src/frontend/src/app/frontcraft/pages/raid/raid.component.html 查看文件

@@ -86,17 +86,17 @@
86 86
                                                     style="white-space: pre-line; max-width: 50vw; word-wrap: break-word;">
87 87
                                                     {{participant.memo}}
88 88
                                                 </p>
89
-                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}}
89
+                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
90 90
                                             </div>
91 91
                                         </ng-template>
92
-                                        <nb-icon *ngIf="participant.memo == null || participant.memo == ''"
93
-                                            [nbPopover]="template" nbPopoverTrigger="hover"
94
-                                            style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
95
-
96
-                                        <nb-icon *ngIf="participant.memo != null && participant.memo != ''"
97
-                                            [nbPopover]="template" nbPopoverTrigger="hover"
98
-                                            style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
92
+                                        <nb-icon
93
+                                        [nbPopover]="template" 
94
+                                        nbPopoverTrigger="hover"
95
+                                        style="width: 0.75em; height: 0.75em;" 
96
+                                        [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
97
+                                        [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
99 98
                                         </nb-icon>
99
+
100 100
                                         <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
101 101
                                             Trial
102 102
                                         </span>
@@ -129,16 +129,15 @@
129 129
                                                 <p style="white-space: pre-line;">
130 130
                                                     {{participant.memo}}
131 131
                                                 </p>
132
-                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}}
132
+                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
133 133
                                             </div>
134 134
                                         </ng-template>
135
-                                        <nb-icon *ngIf="participant.memo == null || participant.memo == ''"
136
-                                            [nbPopover]="template" nbPopoverTrigger="hover"
137
-                                            style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
138
-
139
-                                        <nb-icon *ngIf="participant.memo != null && participant.memo != ''"
140
-                                            [nbPopover]="template" nbPopoverTrigger="hover"
141
-                                            style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
135
+                                        <nb-icon
136
+                                            [nbPopover]="template" 
137
+                                            nbPopoverTrigger="hover"
138
+                                            style="width: 0.75em; height: 0.75em;" 
139
+                                            [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
140
+                                            [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
142 141
                                         </nb-icon>
143 142
                                         <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
144 143
                                             Trial
@@ -174,17 +173,17 @@
174 173
                                                     <p style="white-space: pre-line;">
175 174
                                                         {{participant.memo}}
176 175
                                                     </p>
177
-                                                    {{participant.timestamp | date : 'HH:mm EEE MMM d'}}
176
+                                                    {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
178 177
                                                 </div>
179 178
                                             </ng-template>
180
-                                            <nb-icon *ngIf="participant.memo == null || participant.memo == ''"
181
-                                                [nbPopover]="template" nbPopoverTrigger="hover"
182
-                                                style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
183
-
184
-                                            <nb-icon *ngIf="participant.memo != null && participant.memo != ''"
185
-                                                [nbPopover]="template" nbPopoverTrigger="hover"
186
-                                                style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
179
+                                            <nb-icon
180
+                                                [nbPopover]="template" 
181
+                                                nbPopoverTrigger="hover"
182
+                                                style="width: 0.75em; height: 0.75em;" 
183
+                                                [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
184
+                                                [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
187 185
                                             </nb-icon>
186
+
188 187
                                             <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
189 188
                                                 Trial
190 189
                                             </span>
@@ -213,7 +212,7 @@
213 212
 
214 213
                         <nb-list class="">
215 214
                             <nb-list-item *ngFor="let item of displayedtokens | keyvalue">
216
-                                <table>
215
+                                <table style="width: 100%;">
217 216
                                     <tr>
218 217
                                         <td>
219 218
                                             <wowhead [item]="item.value[0]"></wowhead><br />
@@ -221,30 +220,29 @@
221 220
                                         </td>
222 221
                                     </tr>
223 222
                                     <tr>
224
-                                        <td>
225
-                                            <div class="row">
226
-                                                <div *ngFor="let token of item.value" class="col-12 col-md-6 col-xl-4">
227
-                                                    <span
228
-                                                        [ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }">
229
-                                                        <span style="text-transform: capitalize; white-space: nowrap;">
230
-                                                            [ {{token.level}} ]&nbsp;
231
-                                                            <a [routerLink]="'/frontcraft/character/'+token.charactername"
232
-                                                                [ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}">
233
-                                                                {{token.charactername}}
234
-                                                            </a>
235
-                                                        </span>
223
+                                        <td class="row">
224
+                                            <div *ngFor="let token of item.value" class="col-12 col-md-6 col-xl-4"
225
+                                                style="white-space: nowrap;">
226
+                                                <span
227
+                                                    [ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }">
228
+                                                    <span style="text-transform: capitalize;">
229
+                                                        [ {{token.level}} ]&nbsp;
230
+                                                        <a [routerLink]="'/frontcraft/character/'+token.charactername"
231
+                                                            [ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}">
232
+                                                            {{token.charactername}}
233
+                                                        </a>
236 234
                                                     </span>
237
-                                                    <ng-template #tooltip>
238
-                                                        <div style="color:white">
239
-                                                            Becomes valid when promoted to Raider
240
-                                                        </div>
241
-                                                    </ng-template>
242
-                                                    <span *ngIf="token.rank=='Trial'" style="font-size: 9px;"
243
-                                                        [nbPopover]="tooltip" nbPopoverTrigger="hover"
244
-                                                        nbPopoverPlacement="top">
245
-                                                        Trial
246
-                                                    </span><br />
247
-                                                </div>
235
+                                                </span>
236
+                                                <ng-template #tooltip>
237
+                                                    <div style="color:white">
238
+                                                        Becomes valid when promoted to Raider
239
+                                                    </div>
240
+                                                </ng-template>
241
+                                                <span *ngIf="token.rank=='Trial'" style="font-size: 9px;"
242
+                                                    [nbPopover]="tooltip" nbPopoverTrigger="hover"
243
+                                                    nbPopoverPlacement="top">
244
+                                                    &nbsp;Trial
245
+                                                </span><br />
248 246
                                             </div>
249 247
 
250 248
                                         </td>

+ 193
- 181
src/frontend/src/app/frontcraft/pages/raid/raid.component.ts 查看文件

@@ -1,4 +1,4 @@
1
-import { Component, OnInit } from '@angular/core';
1
+import { Component, OnInit, Injector } from '@angular/core';
2 2
 import { ActivatedRoute, Router } from '@angular/router';
3 3
 import { RaidData, Raid, Signup, Character, Spec, Item, SRToken } from '../../../../../../backend/Types/Types';
4 4
 import { NbToastrService, NbDialogService } from '@nebular/theme';
@@ -6,202 +6,214 @@ import { FrontcraftCharacerpickerComponent } from './characterpicker.component';
6 6
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
7 7
 import { FrontcraftBuyTokenComponent } from '../shop/buytoken.component';
8 8
 import { allItems } from '../../../../../../backend/Types/Items';
9
-import { IApiService } from '../../services/ApiService';
9
+import { IApiService } from '../../services/ApiService/ApiService';
10 10
 import { UpdatingComponent } from '../../UpdatingComponent';
11
-import { LoggerService } from '../../services/logger.service';
11
+import { LoggerService } from '../../services/LoggerService/logger.service';
12
+import { FlashService } from '../../services/flash-service';
12 13
 
13 14
 @Component({
14 15
   selector: 'raid',
15 16
   templateUrl: './raid.component.html',
16 17
 })
17
-export class FrontcraftRaidComponent 
18
-extends UpdatingComponent
19
-implements OnInit{
20
-
21
-    canSignup = false
22
-    isSignedup = false
23
-    islate = false
24
-    isTier = false
25
-    reservesShown = true
26
-    manageRaid
27
-    mySignup: (Signup & Character & Spec)
28
-
29
-    raid: RaidData = <any>{
30
-      participants:{
31
-          Druid: [],
32
-          Hunter: [],
33
-          Mage: [],
34
-          Paladin: [],
35
-          Priest: [],
36
-          Rogue: [],
37
-          Shaman: [],
38
-          Warlock: [],
39
-          Warrior: [],
40
-      },
41
-      tanks: [],
42
-      healers: [],
43
-      tokens:{},
44
-      tier: 'null'
18
+export class FrontcraftRaidComponent
19
+  extends UpdatingComponent
20
+  implements OnInit {
21
+
22
+  now = Date.now()
23
+  canSignup = false
24
+  isSignedup = false
25
+  islate = false
26
+  isTier = false
27
+  reservesShown = true
28
+  manageRaid
29
+  mySignup: (Signup & Character & Spec)
30
+
31
+  raid: RaidData = <any>{
32
+    participants: {
33
+      Druid: [],
34
+      Hunter: [],
35
+      Mage: [],
36
+      Paladin: [],
37
+      Priest: [],
38
+      Rogue: [],
39
+      Shaman: [],
40
+      Warlock: [],
41
+      Warrior: [],
42
+    },
43
+    tanks: [],
44
+    healers: [],
45
+    tokens: {},
46
+    tier: 'null'
47
+  }
48
+  tokens: { [itemname in string]: (Character & SRToken & Item)[] } = {}
49
+  displayedtokens = {}
50
+  search = ""
51
+
52
+  constructor(
53
+    injector: Injector,
54
+    private api: IApiService,
55
+    private route: ActivatedRoute,
56
+    private router: Router,
57
+    private dialogService: NbDialogService,
58
+    private toast: NbToastrService,
59
+    private flash: FlashService,
60
+    private logger: LoggerService
61
+  ) {
62
+    super(injector)
63
+  }
64
+
65
+  ngOnInit() {
66
+    this.manageRaid = this.api.get('manageRaid')
67
+    const signupFeature = this.api.get('signup')
68
+
69
+    if (signupFeature) {
70
+      this.canSignup = true
45 71
     }
46
-    tokens: {[itemname in string]: (Character & SRToken & Item)[]} = {}
47
-    displayedtokens = {}
48
-    search = ""
49
-
50
-    constructor(
51
-      private api: IApiService,
52
-      private route: ActivatedRoute,
53
-      private router: Router,
54
-      private dialogService : NbDialogService,
55
-      private toast: NbToastrService,
56
-      logger: LoggerService
57
-    ){
58
-      super(router, api, logger)
59
-    }
60
-
61
-    ngOnInit(){
62
-      this.manageRaid = this.api.get('manageRaid')
63
-      const signupFeature = this.api.get('signup')
64
-      
65
-      if(signupFeature){
66
-        this.canSignup = true
72
+    this.refresh().then(() => {
73
+      this.subscribe(String(this.raid.id), (data: RaidData) => this.display(data).then(_ =>
74
+        this.flash.show()
75
+      ))
76
+    })
77
+  }
78
+
79
+  itemSelect = async (item) => {
80
+    this.dialogService.open(FrontcraftBuyTokenComponent, {
81
+      context: {
82
+        item: item,
83
+        signup: this.mySignup,
84
+        tier: this.raid.tier,
85
+        characterName: this.mySignup.charactername
67 86
       }
87
+    }).onClose.subscribe(() => {
68 88
       this.refresh().then(() => {
69
-        this.subscribe(String(this.raid.id), (data: RaidData) => this.display(data))
70
-      })
71
-    }
72
-
73
-    itemSelect = async(item) => {
74
-      this.dialogService.open(FrontcraftBuyTokenComponent, {
75
-        context: {
76
-          item: item,
77
-          signup: this.mySignup,
78
-          tier: this.raid.tier,
79
-          characterName: this.mySignup.charactername
80
-        }
81
-      }).onClose.subscribe(() => {
82
-        this.refresh().then(()=>{
83
-          this.reservesShown = true
84
-        })
89
+        this.reservesShown = true
85 90
       })
86
-    }
87
-
88
-    signup = async () => {
89
-      const signupFeature = this.api.get('signup')
90
-      if(!signupFeature) return
91
-
92
-      this.dialogService.open(FrontcraftCharacerpickerComponent, { 
93
-        closeOnBackdropClick: true,
94
-        closeOnEsc: true,
95
-        context: {
96
-          raid: this.raid,
97
-        } 
98
-      }).onClose.subscribe(() => {
99
-        this.refresh().then(()=>{
100
-          this.reservesShown = false
101
-        })
91
+    })
92
+  }
93
+
94
+  signup = async () => {
95
+    const signupFeature = this.api.get('signup')
96
+    if (!signupFeature) return
97
+
98
+    this.dialogService.open(FrontcraftCharacerpickerComponent, {
99
+      closeOnBackdropClick: true,
100
+      closeOnEsc: true,
101
+      context: {
102
+        raid: this.raid,
103
+      }
104
+    }).onClose.subscribe(() => {
105
+      this.refresh().then(() => {
106
+        this.reservesShown = false
102 107
       })
103
-    }
104
-
105
-    async archiveRaid(raid:Raid){
106
-      await this.manageRaid!.archiveRaid(raid)
107
-      this.toast.show('Raid archived', 'Success', { status: 'success' })
108
-      this.router.navigateByUrl('/frontcraft/archive/'+raid.id)
109
-    }
110
-
111
-    async startRaid(raid:Raid){
112
-      await this.manageRaid!.startRaid(raid)
113
-      this.toast.show('Raid started', 'Success', { status: 'success' })
114
-      this.router.navigateByUrl('/frontcraft/archive/'+raid.id)
115
-    }
116
-
117
-    unsign = async () => {
118
-      const signupFeature = this.api.get('signup')
119
-      if(!signupFeature) return
120
-
121
-      await signupFeature.unsign(this.api.getAuth().token.value, {
122
-        ...this.mySignup,
123
-        id: this.mySignup.characterid, 
124
-      }, this.raid)
125
-      this.toast.show('Success', 'Unsigned', { status: 'success' })
126
-    }
127
-
128
-    setLate = async (value:boolean) => {
129
-      const auth = this.api.getAuth()
130
-      const signup = this.api.get('signup')
131
-      if(!signup) return
132
-
133
-      await signup.sign(auth.token.value, {
134
-        ...this.mySignup,
135
-        id: this.mySignup.characterid,
136
-      }, this.raid, value, this.mySignup.memo)
137
-      this.toast.show('Signup', 'Success', { status: 'success' })
138
-    }
139
-
140
-    adminUnsign = async(character:Character) => {
141
-      const manage =  this.api.get('manageRaid')
142
-      if(!manage) return
143
-      await manage.adminUnsign({
144
-        ...character,
145
-        id: character['characterid']
146
-      }, this.raid)
147
-    }
148
-
149
-    refresh = async () => {
150
-      const param = this.route.snapshot.paramMap.get('id');
151
-
152
-      const raidManager = this.api.get('RaidManager')
108
+    })
109
+  }
110
+
111
+  async archiveRaid(raid: Raid) {
112
+    await this.manageRaid!.archiveRaid(raid)
113
+    this.toast.show('Raid archived', 'Success', { status: 'success' })
114
+    this.router.navigateByUrl('/frontcraft/archive/' + raid.id)
115
+  }
116
+
117
+  async startRaid(raid: Raid) {
118
+    await this.manageRaid!.startRaid(raid)
119
+    this.toast.show('Raid started', 'Success', { status: 'success' })
120
+    this.router.navigateByUrl('/frontcraft/archive/' + raid.id)
121
+  }
122
+
123
+  unsign = async () => {
124
+    const signupFeature = this.api.get('signup')
125
+    if (!signupFeature) return
126
+
127
+    await signupFeature.unsign(this.api.getAuth().token.value, {
128
+      ...this.mySignup,
129
+      id: this.mySignup.characterid,
130
+    }, this.raid)
131
+    this.toast.show('Success', 'Unsigned', { status: 'success' })
132
+  }
133
+
134
+  setLate = async (value: boolean) => {
135
+    const auth = this.api.getAuth()
136
+    const signup = this.api.get('signup')
137
+    if (!signup) return
138
+
139
+    await signup.sign(auth.token.value, {
140
+      ...this.mySignup,
141
+      id: this.mySignup.characterid,
142
+    }, this.raid, value, this.mySignup.memo)
143
+    this.toast.show('Signup', 'Success', { status: 'success' })
144
+  }
145
+
146
+  adminUnsign = async (character: Character) => {
147
+    const manage = this.api.get('manageRaid')
148
+    if (!manage) return
149
+    await manage.adminUnsign({
150
+      ...character,
151
+      id: character['characterid']
152
+    }, this.raid)
153
+  }
154
+
155
+  refresh = async () => {
156
+    const param = this.route.snapshot.paramMap.get('id');
157
+    const raidManager = this.api.get('RaidManager')
158
+
159
+    try {
153 160
       const raiddata = await raidManager.getRaidData(<any>{
154 161
         id: param
155 162
       })
156
-      this.display(raiddata)
163
+      await this.display(raiddata)
164
+    } catch (e) {
165
+      this.router.navigate(["/"])
157 166
     }
158
-
159
-    display =  async (raiddata:RaidData)  => {
160
-      this.isTier = allItems[raiddata.tier] != null
161
-
162
-      this.raid = raiddata
163
-      this.tokens = raiddata.tokens;
164
-
165
-      [...raiddata.tanks, ...raiddata.healers, ...Object.values(raiddata.participants).flat()].forEach(p => {
166
-        p['color'] = getClassColor(p.class)
167
-      })
168
-
169
-      const user = this.api.getCurrentUser()
170
-      const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!)     
171
-      
172
-      if(matchingSignup){       
173
-        this.mySignup = matchingSignup
174
-        this.isSignedup = true        
175
-        this.mySignup['status'] = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending'
176
-      }else{
177
-        this.isSignedup = false
178
-        this.mySignup = null
179
-      }
180
-
181
-      this.changeSearch()
167
+  }
168
+
169
+  display = async (raiddata: RaidData) => {
170
+    this.logger.log(raiddata)
171
+    this.isTier = allItems[raiddata.tier] != null
172
+
173
+    this.raid = raiddata
174
+    this.tokens = raiddata.tokens;
175
+
176
+    [...raiddata.tanks, ...raiddata.healers, ...Object.values(raiddata.participants).flat()].forEach(p => {
177
+      p['color'] = getClassColor(p.class)
178
+      p['before'] = Number.parseInt(this.raid.start) - Date.parse(p.timestamp)
179
+    })
180
+
181
+    const user = this.api.getCurrentUser()
182
+    const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!)
183
+
184
+    if (matchingSignup) {
185
+      this.mySignup = matchingSignup
186
+      this.isSignedup = true
187
+      this.mySignup['status'] = matchingSignup['benched'] ? 'Bench' : matchingSignup['late'] ? 'Late' : 'Attending'
188
+    } else {
189
+      this.isSignedup = false
190
+      this.mySignup = null
182 191
     }
183 192
 
184
-    changeSearch(){
185
-      if(!this.search || this.search == ""){
186
-        this.displayedtokens = this.tokens
187
-      }else{
188
-        this.displayedtokens = {}
189
-        Object.entries(this.tokens).forEach((e: [string /*itemname*/, (Character & SRToken & Item)[]]) => {
190
-          const filteredTokens = e[1].filter(item => {
191
-            return item.itemname.toLocaleLowerCase().includes(this.search.toLocaleLowerCase())
192
-          })
193
-          if(filteredTokens.length > 0)
194
-            this.displayedtokens[e[0]] = filteredTokens 
195
-        })        
196
-      }
197
-    }
198
-
199
-    setBench(signup:Signup){
200
-      this.manageRaid.setBenched({
201
-        characterid: signup.characterid,
202
-        raidid: signup.raidid,
203
-        late: false,
204
-        benched: !signup.benched
205
-      }).then(_ => this.refresh())
193
+    this.changeSearch()
194
+  }
195
+
196
+  changeSearch() {
197
+    if (!this.search || this.search == "") {
198
+      this.displayedtokens = this.tokens
199
+    } else {
200
+      this.displayedtokens = {}
201
+      Object.entries(this.tokens).forEach((e: [string /*itemname*/, (Character & SRToken & Item)[]]) => {
202
+        const filteredTokens = e[1].filter(item => {
203
+          return item.itemname.toLocaleLowerCase().includes(this.search.toLocaleLowerCase())
204
+        })
205
+        if (filteredTokens.length > 0)
206
+          this.displayedtokens[e[0]] = filteredTokens
207
+      })
206 208
     }
209
+  }
210
+
211
+  setBench(signup: Signup) {
212
+    this.manageRaid.setBenched({
213
+      characterid: signup.characterid,
214
+      raidid: signup.raidid,
215
+      late: false,
216
+      benched: !signup.benched
217
+    }).then(_ => this.refresh())
218
+  }
207 219
 }

+ 3
- 4
src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts 查看文件

@@ -1,8 +1,8 @@
1 1
 import { Component, OnInit } from '@angular/core';
2 2
 import { Raid, RaidData } from '../../../../../../backend/Types/Types';
3
-import { NbDialogRef } from '@nebular/theme';
3
+import { NbDialogRef, DARK_THEME } from '@nebular/theme';
4 4
 import { Tiers, _Tiers } from '../../../../../../backend/Types/Items';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6 6
 
7 7
 const ONE_MINUTE = 60000
8 8
 const ONE_HOUR = 60 * ONE_MINUTE
@@ -34,7 +34,6 @@ export class FrontcraftCreateRaidsComponent implements OnInit {
34 34
   }
35 35
 
36 36
   onTierSelect(){
37
-    
38 37
   }
39 38
 
40 39
   onTemplateSelect(){
@@ -62,7 +61,7 @@ export class FrontcraftCreateRaidsComponent implements OnInit {
62 61
     const manage = this.api.get('manageRaid')
63 62
     if(!manage) return
64 63
 
65
-    await manage.createRaid(raid)
64
+    const dbraid = await manage.createRaid(raid)
66 65
     this.dialogRef.close()
67 66
   }
68 67
 }

+ 38
- 32
src/frontend/src/app/frontcraft/pages/raids/raids.component.html 查看文件

@@ -6,30 +6,33 @@
6 6
   <nb-list nbInfiniteList listenWindowScroll [threshold]="500">
7 7
     <nb-list-item *ngFor="let raid of raids" class="raidlist">
8 8
       <a [routerLink]="'/frontcraft/raid/'+raid.id">
9
-        <div class="row">
10
-          <table>
11
-            <tr>
12
-              <td rowspan="2" style="padding-right: 20px;">
13
-                <img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
14
-                  style="max-width: 50px; object-fit: contain" />
15
-              </td>
9
+        <table>
10
+          <tr>
11
+            <td rowspan="2" style="padding-right: 20px;">
12
+              <img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
13
+                style="max-width: 50px; object-fit: contain" />
14
+            </td>
15
+            <td>
16 16
               <h4>
17 17
                 {{raid.title}}
18 18
               </h4>
19
-            </tr>
20
-            <tr>
21
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
22
-                <nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{raid.signupcount}}&nbsp;/&nbsp;{{raid.size}}<br>
23
-              </td>
24
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
25
-                <nb-icon icon="clock-outline"></nb-icon>&nbsp;{{raid.start | date : 'HH:mm'}}
26
-              </td>
27
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
28
-                <nb-icon icon="calendar-outline"></nb-icon>&nbsp;{{raid.start | date : 'EEE MMM d'}}
29
-              </td>
30
-            </tr>
31
-          </table>
32
-        </div>
19
+            </td>
20
+          </tr>
21
+          <tr class="row">
22
+            <td class="col-3" style="padding-left:8px; padding-right: 25px; white-space: nowrap; color: lightslategray;">
23
+              <nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{ raid.signupcount | number:'2.0' }}&nbsp;/&nbsp;{{raid.size}}
24
+            </td>
25
+            <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
26
+              <nb-icon icon="person-remove-outline"></nb-icon>&nbsp;{{ raid.benchcount }}
27
+            </td>
28
+            <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
29
+              <nb-icon icon="clock-outline"></nb-icon>&nbsp;{{raid.start | date : 'HH:mm'}}
30
+            </td>
31
+            <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
32
+              <nb-icon icon="calendar-outline"></nb-icon>&nbsp;{{raid.start | date : 'EEE MMM d'}}
33
+            </td>
34
+          </tr>
35
+        </table>
33 36
       </a>
34 37
     </nb-list-item>
35 38
   </nb-list>
@@ -40,30 +43,33 @@
40 43
   <nb-list nbInfiniteList listenWindowScroll [threshold]="500">
41 44
     <nb-list-item *ngFor="let raid of oldraids" [routerLink]="'/frontcraft/archive/'+raid.id">
42 45
       <a [routerLink]="'/frontcraft/archive/'+raid.id">
43
-        <div class="row">
44
-          <table>
46
+          <table style="width: 100%;">
45 47
             <tr>
46 48
               <td rowspan="2" style="padding-right: 20px;">
47 49
                 <img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
48 50
                   style="max-width: 50px;;object-fit: contain; filter: grayscale(75%)" />
49 51
               </td>
50
-              <h4>
51
-                {{raid.title}}
52
-              </h4>
52
+              <td>
53
+                <h4>
54
+                  {{raid.title}}
55
+                </h4>
56
+              </td>
53 57
             </tr>
54
-            <tr>
55
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
56
-                <nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{raid.signupcount}}&nbsp;/&nbsp;{{raid.size}}<br>
58
+            <tr class="row">
59
+              <td class="col-3" style="padding-left:8px; padding-right: 25px; white-space: nowrap; color: lightslategray;">
60
+                <nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{ raid.signupcount | number:'2.0' }}&nbsp;/&nbsp;{{raid.size}}
61
+              </td>
62
+              <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
63
+                <nb-icon icon="person-remove-outline"></nb-icon>&nbsp;{{ raid.participants.bench.length }}
57 64
               </td>
58
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
65
+              <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
59 66
                 <nb-icon icon="clock-outline"></nb-icon>&nbsp;{{raid.start | date : 'HH:mm'}}
60 67
               </td>
61
-              <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
68
+              <td class="col-3" style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
62 69
                 <nb-icon icon="calendar-outline"></nb-icon>&nbsp;{{raid.start | date : 'EEE MMM d'}}
63 70
               </td>
64 71
             </tr>
65 72
           </table>
66
-        </div>
67 73
       </a>
68 74
     </nb-list-item>
69 75
   </nb-list>

+ 15
- 12
src/frontend/src/app/frontcraft/pages/raids/raids.component.ts 查看文件

@@ -1,19 +1,20 @@
1
-import { Component, OnInit, OnDestroy } from '@angular/core';
2
-import { NbDialogService } from '@nebular/theme';
1
+import { Component, OnInit, OnDestroy, Injector } from '@angular/core';
2
+import { NbDialogService, NbToastrService } from '@nebular/theme';
3 3
 import { FrontcraftCreateRaidsComponent } from './createraid.compontent';
4 4
 import { Router, NavigationStart } from '@angular/router';
5
-import { IApiService } from '../../services/ApiService';
5
+import { IApiService } from '../../services/ApiService/ApiService';
6 6
 import { UpdatingComponent } from '../../UpdatingComponent';
7
-import { LoggerService } from '../../services/logger.service';
7
+import { LoggerService } from '../../services/LoggerService/logger.service';
8
+import { FlashService } from '../../services/flash-service';
8 9
 
9 10
 @Component({
10 11
   selector: 'raids',
11 12
   templateUrl: 'raids.component.html',
12 13
   styleUrls: ['raids.component.scss'],
13 14
 })
14
-export class FrontcraftRaidsComponent 
15
-extends UpdatingComponent
16
-implements OnInit {
15
+export class FrontcraftRaidsComponent
16
+  extends UpdatingComponent
17
+  implements OnInit {
17 18
 
18 19
 
19 20
   manageRaid
@@ -22,20 +23,22 @@ implements OnInit {
22 23
   pageSize = 10;
23 24
 
24 25
   constructor(
26
+    injector: Injector,
25 27
     private api: IApiService,
26
-    router: Router,
27 28
     private dialogService: NbDialogService,
28
-    logger: LoggerService
29
+    private flash: FlashService,
29 30
   ) {
30
-    super(router, api, logger)
31
+    super(injector)
31 32
   }
32 33
 
33
-  ngOnDestroy(){
34
+  ngOnDestroy() {
34 35
     super.ngOnDestroy()
35 36
   }
36 37
 
37 38
   async ngOnInit() {
38
-    this.subscribe("raids", () => this.refresh())
39
+    this.subscribe("raids", () => this.refresh().then(_ =>
40
+      this.flash.show()
41
+    ))
39 42
 
40 43
     this.manageRaid = this.api.get('manageRaid')
41 44
     this.refresh()

+ 1
- 1
src/frontend/src/app/frontcraft/pages/rules/rules.component.ts 查看文件

@@ -1,6 +1,6 @@
1 1
 import { Component, OnInit } from '@angular/core';
2 2
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
3
-import { IApiService } from '../../services/ApiService';
3
+import { IApiService } from '../../services/ApiService/ApiService';
4 4
 
5 5
 @Component({
6 6
   selector: 'rules',

+ 1
- 1
src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts 查看文件

@@ -3,7 +3,7 @@ import { NbToastrService, NbDialogRef } from '@nebular/theme';
3 3
 import { Item, Character, SRToken, Signup } from '../../../../../../backend/Types/Types';
4 4
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5 5
 import { _Tiers, Tiers } from '../../../../../../backend/Types/Items';
6
-import { IApiService } from '../../services/ApiService';
6
+import { IApiService } from '../../services/ApiService/ApiService';
7 7
 
8 8
 @Component({
9 9
     selector: 'buyToken',

+ 2
- 2
src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts 查看文件

@@ -1,7 +1,7 @@
1 1
 import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
2
-import { ClientApiService } from '../../services/client-login-api';
2
+import { ClientApiService } from '../../services/ApiService/client-login-api';
3 3
 import { Item, Character } from '../../../../../../backend/Types/Types';
4
-import { IApiService } from '../../services/ApiService';
4
+import { IApiService } from '../../services/ApiService/ApiService';
5 5
 
6 6
 @Component({
7 7
   selector: 'itemselect',

+ 1
- 1
src/frontend/src/app/frontcraft/pages/shop/wowhead.component.ts 查看文件

@@ -8,7 +8,7 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
8 8
   template: `
9 9
     <span *ngIf="tooltipHtml">
10 10
       <ng-template #tooltip>
11
-        <div style="color:white" [innerHTML]="tooltipHtml">
11
+        <div style="color:white; background-color:black; padding: 7px" [innerHTML]="tooltipHtml">
12 12
         </div>
13 13
       </ng-template>
14 14
       <span 

+ 1
- 1
src/frontend/src/app/frontcraft/pages/user/user.component.ts 查看文件

@@ -5,7 +5,7 @@ import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5 5
 import { _Tiers } from '../../../../../../backend/Types/Items';
6 6
 import { _Rank } from '../../../../../../backend/Types/Types';
7 7
 import { NbToastrService } from '@nebular/theme';
8
-import { hash, IApiService } from '../../services/ApiService';
8
+import { hash, IApiService } from '../../services/ApiService/ApiService';
9 9
 
10 10
 @Component({
11 11
   selector: 'user-component',

+ 1
- 1
src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts 查看文件

@@ -1,7 +1,7 @@
1 1
 import { Component, OnInit } from '@angular/core';
2 2
 import { _Rank, _Class, _Race, RPCPermission  } from '../../../../../../backend/Types/Types'
3 3
 import { NbToastrService } from '@nebular/theme';
4
-import { IApiService } from '../../services/ApiService';
4
+import { IApiService } from '../../services/ApiService/ApiService';
5 5
 
6 6
 
7 7
 @Component({

src/frontend/src/app/frontcraft/services/ApiService.ts → src/frontend/src/app/frontcraft/services/ApiService/ApiService.ts 查看文件

@@ -1,5 +1,5 @@
1
-import { Auth, FrontcraftIfc, FrontcraftFeatureIfc, User, SomeOf } from '../../../../../backend/Types/Types';
2
-import { saltedHash } from '../../../../../backend/Util/hash';
1
+import { Auth, FrontcraftIfc, FrontcraftFeatureIfc, User, SomeOf } from '../../../../../../backend/Types/Types';
2
+import { saltedHash } from '../../../../../../backend/Util/hash';
3 3
 import { RPCSocket, ConnectedSocket } from 'rpclibrary';
4 4
 
5 5
 export class IApiService{
@@ -9,7 +9,6 @@ export class IApiService{
9 9
     initialize: () => Promise<any>
10 10
     getCurrentUser: () => User
11 11
     login: (username: string, password: string) => Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>>
12
-    connectShoutbox: (callback:Function) => Promise<Function>
13 12
     kick: () => Promise<void>
14 13
     logout: () => Promise<void>
15 14
     get: <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) => K extends keyof FrontcraftIfc

src/frontend/src/app/frontcraft/services/client-login-api.ts → src/frontend/src/app/frontcraft/services/ApiService/client-login-api.ts 查看文件

@@ -1,10 +1,9 @@
1 1
 import { Injectable } from "@angular/core";
2 2
 import { RPCSocket } from 'rpclibrary/js/src/Frontend'
3 3
 import { ConnectedSocket } from "rpclibrary"
4
-import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
5
-import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface';
4
+import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../../backend/Types/Types'
6 5
 import { hash, IApiService, GuestUser } from './ApiService';
7
-import { LoggerService } from './logger.service';
6
+import { LoggerService } from '../LoggerService/logger.service';
8 7
 
9 8
 declare const Cookies
10 9
 
@@ -12,7 +11,7 @@ declare const Cookies
12 11
 export class ClientApiService implements IApiService{
13 12
     private socket:ConnectedSocket<FrontcraftIfc>
14 13
     private auth:Auth
15
-    private privSocket: RPCSocket<SomeOf<FrontcraftFeatureIfc>>
14
+    private privSocket: ConnectedSocket<SomeOf<FrontcraftFeatureIfc>>
16 15
 
17 16
     constructor(
18 17
         private logger: LoggerService
@@ -22,7 +21,7 @@ export class ClientApiService implements IApiService{
22 21
 
23 22
     getUnprivilegedSocket = () : ConnectedSocket<FrontcraftIfc> => this.socket
24 23
 
25
-    private getPrivilegedSocket = async (auth:Auth) : Promise<RPCSocket<SomeOf<FrontcraftFeatureIfc>>> => {
24
+    private getPrivilegedSocket = async (auth:Auth) : Promise<ConnectedSocket<SomeOf<FrontcraftFeatureIfc>>> => {
26 25
         if(this.privSocket) {
27 26
             return this.privSocket
28 27
         }
@@ -105,11 +104,6 @@ export class ClientApiService implements IApiService{
105 104
         return sock
106 105
     }
107 106
 
108
-    async connectShoutbox(callback:Function) : Promise<Function>{
109
-        const uuid = await this.get('Shoutbox').subscribe(callback)
110
-        return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(uuid, msg)
111
-    }
112
-
113 107
     kick = async () => {
114 108
         await this.logout()
115 109
         location.reload()

src/frontend/src/app/frontcraft/services/server-login-api.ts → src/frontend/src/app/frontcraft/services/ApiService/server-login-api.ts 查看文件

@@ -1,45 +1,48 @@
1 1
 import { Injectable } from "@angular/core";
2
-import {RPCSocket} from 'rpclibrary/js/src/Frontend'
3
-import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
2
+import { RPCSocket } from 'rpclibrary/js/src/Frontend'
3
+import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../../backend/Types/Types'
4 4
 import { hash, IApiService, GuestUser } from './ApiService';
5
-import { LoggerService } from './logger.service';
5
+import { LoggerService } from '../LoggerService/logger.service';
6 6
 import { ConnectedSocket } from 'rpclibrary';
7
+import { async } from '@angular/core/testing';
7 8
 
8 9
 declare const Cookies
9 10
 
10 11
 @Injectable()
11
-export class ServerApiService implements IApiService{
12
-    private socket:ConnectedSocket<FrontcraftIfc>
13
-    private auth:Auth
12
+export class ServerApiService implements IApiService {
13
+    private socket: ConnectedSocket<FrontcraftIfc>
14
+    private auth: Auth
14 15
     private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc>
15 16
 
16 17
     constructor(
17 18
         private logger: LoggerService
18
-    ){}
19
+    ) { }
19 20
 
20
-    getUnprivilegedSocket = () : ConnectedSocket<FrontcraftIfc> => this.socket
21
+    getUnprivilegedSocket = (): ConnectedSocket<FrontcraftIfc> => this.socket
22
+
23
+    get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature: K):
24
+        K extends keyof FrontcraftIfc ? FrontcraftIfc[K] :
25
+        K extends keyof FrontcraftFeatureIfc ? (FrontcraftFeatureIfc[K] | void) :
26
+        never => {
21 27
 
22
-    get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]:
23
-                                                                                        K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): 
24
-                                                                                        never => {
25 28
         //@ts-ignore
26 29
         return this.socket[feature]
27 30
     }
28 31
 
29
-    getCurrentUser = () : User => {
32
+    getCurrentUser = (): User => {
30 33
         return GuestUser
31 34
     }
32 35
 
33
-    login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
36
+    login = async (username: string, password: string): Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
34 37
         const pwHash = await hash(password)
35 38
         let auth
36
-        try{
39
+        try {
37 40
             auth = await this.socket.UserManager.login(username, pwHash)
38
-        }catch(e){
41
+        } catch (e) {
39 42
             return
40 43
         }
41 44
 
42
-        if(!auth){ 
45
+        if (!auth) {
43 46
             await this.logout()
44 47
             throw new Error("Login failed")
45 48
         }
@@ -51,7 +54,7 @@ export class ServerApiService implements IApiService{
51 54
 
52 55
     }
53 56
 
54
-    async connectShoutbox(callback:Function) : Promise<Function>{
57
+    async connectShoutbox(callback: Function): Promise<Function> {
55 58
         return this.logger.log
56 59
     }
57 60
 
@@ -60,32 +63,32 @@ export class ServerApiService implements IApiService{
60 63
 
61 64
     logout = async () => {
62 65
         Cookies.remove('token')
63
-        if(this.auth){
64
-            try{
66
+        if (this.auth) {
67
+            try {
65 68
                 await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value)
66
-            }catch(e){
69
+            } catch (e) {
67 70
                 this.logger.warn(e);
68 71
             }
69
-        } 
72
+        }
70 73
 
71
-        if(this.privSocket) this.privSocket.destroy()
74
+        if (this.privSocket) this.privSocket.destroy()
72 75
         this.privSocket = null
73
-        this.auth  = null
76
+        this.auth = null
74 77
     }
75 78
 
76
-    initialize = async () : Promise<any> => {
77
-        if(this.socket){
79
+    initialize = async (): Promise<any> => {
80
+        if (this.socket) {
78 81
             this.socket.destroy()
79 82
             this.socket = null
80 83
         }
81 84
 
82
-        try{
85
+        try {
83 86
             let conn = new RPCSocket<FrontcraftIfc>(20000, window.location.hostname)
84 87
             conn.on('close', async () => {
85
-            })                        
88
+            })
86 89
 
87 90
             this.socket = await conn.connect()
88
-        }catch(e){
91
+        } catch (e) {
89 92
             this.logger.log(e);
90 93
             throw new Error("Websocket cannot connect")
91 94
         }
@@ -93,10 +96,10 @@ export class ServerApiService implements IApiService{
93 96
 
94 97
     getAuth = () => this.auth
95 98
 
96
-    async checkLogin() : Promise<boolean>{
97
-        if(!this.auth) return false
99
+    async checkLogin(): Promise<boolean> {
100
+        if (!this.auth) return false
98 101
         const valid = this.socket.UserManager.checkToken(this.auth.token.value, this.auth.user.rank)
99
-        if(valid) return true
102
+        if (valid) return true
100 103
         await this.logout()
101 104
         return false
102 105
     }

+ 10
- 0
src/frontend/src/app/frontcraft/services/LoggerService/LoggerService.ts 查看文件

@@ -0,0 +1,10 @@
1
+export interface ILoggerService {
2
+    log,
3
+    warn,
4
+    error,
5
+    table,
6
+}
7
+
8
+export interface ICollapsableLoggerService extends ILoggerService{
9
+    collapsed
10
+}

+ 30
- 0
src/frontend/src/app/frontcraft/services/LoggerService/logger.service.ts 查看文件

@@ -0,0 +1,30 @@
1
+import { Injectable, SkipSelf, Optional } from "@angular/core";
2
+import { environment } from '../../../../environments/environment';
3
+import { ICollapsableLoggerService, ILoggerService } from './LoggerService';
4
+
5
+@Injectable()
6
+export class LoggerService implements ICollapsableLoggerService {
7
+
8
+    private logger = console
9
+    
10
+    constructor(
11
+    ) { }
12
+
13
+    log = (...args) => { this.LOG('log', args) }
14
+    warn = (...args) => { this.LOG('warn', args) }
15
+    error = (...args) => { this.LOG('error', args) }
16
+    table = (...args) => { this.LOG('table', args) }
17
+    collapsed = (groupname: string, type: keyof ILoggerService, ...args) => {
18
+        if (!environment.production) {
19
+            console.groupCollapsed(groupname)
20
+            this[type].apply(this.logger, args)
21
+            console.groupEnd()
22
+        }
23
+    }
24
+
25
+    private LOG(type, ...args) {
26
+        if (!environment.production) {
27
+            this.logger[type].apply(this.logger, ...args)
28
+        }
29
+    }
30
+}

+ 21
- 0
src/frontend/src/app/frontcraft/services/flash-service.ts 查看文件

@@ -0,0 +1,21 @@
1
+import { Injectable, Inject } from '@angular/core';
2
+import { DOCUMENT } from '@angular/common';
3
+
4
+@Injectable()
5
+export class FlashService {
6
+    private displaying = false
7
+    constructor(@Inject(DOCUMENT) private document: Document) { }
8
+
9
+    show(durationMs = 250) {
10
+        if (this.displaying) return
11
+        this.displaying = true
12
+        this.document.getElementById('glowbox').classList.toggle('glowing');
13
+        this.document.getElementById('glowbox').classList.toggle('not-glowing');
14
+
15
+        setTimeout(() => {
16
+            this.displaying = false
17
+            this.document.getElementById('glowbox').classList.toggle('glowing');
18
+            this.document.getElementById('glowbox').classList.toggle('not-glowing');
19
+        }, durationMs)
20
+    }
21
+}

+ 0
- 37
src/frontend/src/app/frontcraft/services/logger.service.ts 查看文件

@@ -1,37 +0,0 @@
1
-import { Injectable } from "@angular/core";
2
-import { environment } from '../../../environments/environment';
3
-
4
-interface ILoggerService{
5
-    log,
6
-    warn,
7
-    error,
8
-    table,
9
-    collapsed
10
-}
11
-
12
-@Injectable()
13
-export class LoggerService implements ILoggerService {
14
-
15
-    constructor(
16
-        private logger = console
17
-    ) { }
18
-
19
-    log = (...args) => { this.LOG('log', args) }
20
-    warn = (...args) => { this.LOG('warn', args) }
21
-    error = (...args) => { this.LOG('error', args) }
22
-    table = (...args) => { this.LOG('table', args) }
23
-    collapsed = (groupname: string, type: keyof ILoggerService, ...args) => {
24
-        if(type === "collapsed")
25
-            return this.log(args)
26
-            
27
-        console.groupCollapsed(groupname)
28
-        this[type as any].apply(this.logger, args)
29
-        console.groupEnd() 
30
-    }
31
-
32
-    private LOG(type, ...args) {
33
-        if (!environment.production) {
34
-            this.logger[type].apply(this.logger, ...args)
35
-        }
36
-    }
37
-}

+ 52
- 27
src/frontend/src/index.html 查看文件

@@ -1,35 +1,57 @@
1 1
 <!doctype html>
2 2
 <html>
3
+
3 4
 <head>
4 5
   <meta charset="utf-8">
5 6
   <title>Frontcraft</title>
6 7
   <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat">
7 8
   <style>
8
-  @import url('https://fonts.googleapis.com/css?family=Montserrat');
9
-
10
-  .loadTitle {
11
-    font-family: 'Montserrat';
12
-    text-align: center;
13
-    color: #FFF;
14
-    display: flex;
15
-    flex-direction: column;
16
-    align-items: center;
17
-    justify-content: center;
18
-    letter-spacing: 1px;
19
-    padding-top: calc(50vh - 120px);
20
-  }
21
-
22
-  .loadH {
23
-    background-image: url('https://media.giphy.com/media/26BROrSHlmyzzHf3i/giphy.gif');
24
-    background-size: cover;
25
-    color: transparent;
26
-    background-clip: text;
27
-    -moz-background-clip: text;
28
-    -webkit-background-clip: text;
29
-    text-transform: uppercase;
30
-    font-size: min(10vw, 120px);
31
-    margin: 10px 0;
32
-  }
9
+    @import url('https://fonts.googleapis.com/css?family=Montserrat');
10
+
11
+    .loadTitle {
12
+      font-family: 'Montserrat';
13
+      text-align: center;
14
+      color: #FFF;
15
+      display: flex;
16
+      flex-direction: column;
17
+      align-items: center;
18
+      justify-content: center;
19
+      letter-spacing: 1px;
20
+      padding-top: calc(50vh - 120px);
21
+    }
22
+
23
+    .loadH {
24
+      background-image: url('https://media.giphy.com/media/26BROrSHlmyzzHf3i/giphy.gif');
25
+      background-size: cover;
26
+      color: transparent;
27
+      background-clip: text;
28
+      -moz-background-clip: text;
29
+      -webkit-background-clip: text;
30
+      text-transform: uppercase;
31
+      font-size: min(10vw, 120px);
32
+      margin: 10px 0;
33
+    }
34
+
35
+    #glowbox {
36
+      pointer-events: none;
37
+      z-index: 10000000;
38
+      position: absolute;
39
+      width: 100%;
40
+      height: 100%;
41
+      -webkit-box-shadow: inset 0px 33px 38px -30px rgba(255, 255, 255, 0.71);
42
+      -moz-box-shadow: inset 0px 33px 38px -30px rgba(255, 255, 255, 0.71);
43
+      box-shadow: inset 0px 33px 38px -30px rgba(255, 255, 255, 0.71);
44
+    }
45
+
46
+    #glowbox.not-glowing {
47
+      opacity: 0;
48
+      transition: opacity 0.25s; 
49
+    }
50
+
51
+    #glowbox.glowing {
52
+      opacity: 1;
53
+      transition: opacity 0.25s; 
54
+    }
33 55
   </style>
34 56
 
35 57
   <base href="/">
@@ -38,13 +60,16 @@
38 60
   <link rel="icon" type="image/png" href="favicon.png">
39 61
   <link rel="icon" type="image/x-icon" href="favicon.ico">
40 62
 </head>
63
+
41 64
 <body>
65
+  <div id="glowbox" class="not-glowing"></div>
42 66
   <ngx-app></ngx-app>
43 67
   <div id="nb-global-spinner" class="spinner" style="background-color: #111; height: 100vh;">
44 68
     <div class="loadTitle">
45 69
       <h1 class="loadH">Tranquil</h1>
46 70
     </div>
47 71
   </div>
48
-  
72
+
49 73
 </body>
50
-</html>
74
+
75
+</html>

+ 3
- 3
src/frontend/src/main.server.ts 查看文件

@@ -3,7 +3,7 @@ import { enableProdMode } from '@angular/core';
3 3
 enableProdMode()
4 4
 
5 5
 export { AppServerModule } from './app/app.server.module';
6
-import { IApiService } from './app/frontcraft/services/ApiService';
6
+import { IApiService } from './app/frontcraft/services/ApiService/ApiService';
7 7
 export { IApiService }
8
-export { ServerApiService } from './app/frontcraft/services/server-login-api';
9
-export { LoggerService } from './app/frontcraft/services/logger.service'
8
+export { ServerApiService } from './app/frontcraft/services/ApiService/server-login-api';
9
+export { LoggerService } from './app/frontcraft/services/LoggerService/logger.service'

+ 3
- 3
src/frontend/src/main.ts 查看文件

@@ -8,9 +8,9 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
8 8
 
9 9
 import { FrontcraftAppModule } from './app/app.module';
10 10
 import { environment } from './environments/environment';
11
-import { IApiService } from "./app/frontcraft/services/ApiService"
12
-import { ClientApiService } from './app/frontcraft/services/client-login-api';
13
-import { LoggerService } from './app/frontcraft/services/logger.service';
11
+import { IApiService } from "./app/frontcraft/services/ApiService/ApiService"
12
+import { ClientApiService } from './app/frontcraft/services/ApiService/client-login-api';
13
+import { LoggerService } from './app/frontcraft/services/LoggerService/logger.service';
14 14
 
15 15
 if (environment.production) {
16 16
   enableProdMode();

Loading…
取消
儲存