mirror of
https://github.com/20kaushik02/spotify-manager-web.git
synced 2025-12-06 09:54:07 +00:00
a lil abstraction, styling
This commit is contained in:
parent
f471c666e7
commit
8898fa0b9f
134
package-lock.json
generated
134
package-lock.json
generated
@ -4041,9 +4041,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/express-serve-static-core": {
|
"node_modules/@types/express-serve-static-core": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.3.tgz",
|
||||||
"integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==",
|
"integrity": "sha512-JEhMNwUJt7bw728CydvYzntD0XJeTmDnvwLlbfbAhE7Tbslm/ax6bdIiUwTgeVlZTsJQPwZwKpAkyDtIjsvx3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@ -4137,9 +4137,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.10.2",
|
"version": "22.10.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
|
||||||
"integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==",
|
"integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.20.0"
|
||||||
@ -7473,9 +7473,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.23.8",
|
"version": "1.23.9",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.8.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
|
||||||
"integrity": "sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==",
|
"integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-buffer-byte-length": "^1.0.2",
|
"array-buffer-byte-length": "^1.0.2",
|
||||||
@ -7489,10 +7489,11 @@
|
|||||||
"es-define-property": "^1.0.1",
|
"es-define-property": "^1.0.1",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"es-object-atoms": "^1.0.0",
|
"es-object-atoms": "^1.0.0",
|
||||||
"es-set-tostringtag": "^2.0.3",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
"es-to-primitive": "^1.3.0",
|
"es-to-primitive": "^1.3.0",
|
||||||
"function.prototype.name": "^1.1.8",
|
"function.prototype.name": "^1.1.8",
|
||||||
"get-intrinsic": "^1.2.6",
|
"get-intrinsic": "^1.2.7",
|
||||||
|
"get-proto": "^1.0.0",
|
||||||
"get-symbol-description": "^1.1.0",
|
"get-symbol-description": "^1.1.0",
|
||||||
"globalthis": "^1.0.4",
|
"globalthis": "^1.0.4",
|
||||||
"gopd": "^1.2.0",
|
"gopd": "^1.2.0",
|
||||||
@ -7513,11 +7514,12 @@
|
|||||||
"object-inspect": "^1.13.3",
|
"object-inspect": "^1.13.3",
|
||||||
"object-keys": "^1.1.1",
|
"object-keys": "^1.1.1",
|
||||||
"object.assign": "^4.1.7",
|
"object.assign": "^4.1.7",
|
||||||
"own-keys": "^1.0.0",
|
"own-keys": "^1.0.1",
|
||||||
"regexp.prototype.flags": "^1.5.3",
|
"regexp.prototype.flags": "^1.5.3",
|
||||||
"safe-array-concat": "^1.1.3",
|
"safe-array-concat": "^1.1.3",
|
||||||
"safe-push-apply": "^1.0.0",
|
"safe-push-apply": "^1.0.0",
|
||||||
"safe-regex-test": "^1.1.0",
|
"safe-regex-test": "^1.1.0",
|
||||||
|
"set-proto": "^1.0.0",
|
||||||
"string.prototype.trim": "^1.2.10",
|
"string.prototype.trim": "^1.2.10",
|
||||||
"string.prototype.trimend": "^1.0.9",
|
"string.prototype.trimend": "^1.0.9",
|
||||||
"string.prototype.trimstart": "^1.0.8",
|
"string.prototype.trimstart": "^1.0.8",
|
||||||
@ -7605,14 +7607,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-set-tostringtag": {
|
"node_modules/es-set-tostringtag": {
|
||||||
"version": "2.0.3",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
"integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.4",
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
"has-tostringtag": "^1.0.2",
|
"has-tostringtag": "^1.0.2",
|
||||||
"hasown": "^2.0.1"
|
"hasown": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -9039,21 +9042,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||||
"integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==",
|
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
"dunder-proto": "^1.0.0",
|
|
||||||
"es-define-property": "^1.0.1",
|
"es-define-property": "^1.0.1",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"es-object-atoms": "^1.0.0",
|
"es-object-atoms": "^1.0.0",
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
|
"get-proto": "^1.0.0",
|
||||||
"gopd": "^1.2.0",
|
"gopd": "^1.2.0",
|
||||||
"has-symbols": "^1.1.0",
|
"has-symbols": "^1.1.0",
|
||||||
"hasown": "^2.0.2",
|
"hasown": "^2.0.2",
|
||||||
"math-intrinsics": "^1.0.0"
|
"math-intrinsics": "^1.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -9077,6 +9080,19 @@
|
|||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-stream": {
|
"node_modules/get-stream": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||||
@ -9850,12 +9866,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/is-async-function": {
|
"node_modules/is-async-function": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz",
|
||||||
"integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
|
"integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-tostringtag": "^1.0.0"
|
"call-bound": "^1.0.3",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"safe-regex-test": "^1.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -10025,12 +10044,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-generator-function": {
|
"node_modules/is-generator-function": {
|
||||||
"version": "1.0.10",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
|
||||||
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
|
"integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-tostringtag": "^1.0.0"
|
"call-bound": "^1.0.3",
|
||||||
|
"get-proto": "^1.0.0",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"safe-regex-test": "^1.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@ -10432,16 +10454,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/iterator.prototype": {
|
"node_modules/iterator.prototype": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
|
||||||
"integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==",
|
"integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.4",
|
"define-data-property": "^1.1.4",
|
||||||
"es-object-atoms": "^1.0.0",
|
"es-object-atoms": "^1.0.0",
|
||||||
"get-intrinsic": "^1.2.6",
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"get-proto": "^1.0.0",
|
||||||
"has-symbols": "^1.1.0",
|
"has-symbols": "^1.1.0",
|
||||||
"reflect.getprototypeof": "^1.0.8",
|
|
||||||
"set-function-name": "^2.0.2"
|
"set-function-name": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -14788,18 +14810,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
"integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==",
|
"integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.8",
|
"call-bind": "^1.0.8",
|
||||||
"define-properties": "^1.2.1",
|
"define-properties": "^1.2.1",
|
||||||
"dunder-proto": "^1.0.1",
|
"es-abstract": "^1.23.9",
|
||||||
"es-abstract": "^1.23.6",
|
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"get-intrinsic": "^1.2.6",
|
"es-object-atoms": "^1.0.0",
|
||||||
"gopd": "^1.2.0",
|
"get-intrinsic": "^1.2.7",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
"which-builtin-type": "^1.2.1"
|
"which-builtin-type": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -14848,14 +14870,16 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||||
"integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
|
"integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.7",
|
"call-bind": "^1.0.8",
|
||||||
"define-properties": "^1.2.1",
|
"define-properties": "^1.2.1",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
"set-function-name": "^2.0.2"
|
"set-function-name": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -15277,9 +15301,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.83.0",
|
"version": "1.83.1",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.83.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.83.1.tgz",
|
||||||
"integrity": "sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw==",
|
"integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
@ -15656,6 +15680,20 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-proto": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import { ReactFlowProvider } from "@xyflow/react";
|
|||||||
// Contexts
|
// Contexts
|
||||||
export const WidthContext = createContext(0);
|
export const WidthContext = createContext(0);
|
||||||
export const AuthContext = createContext(false);
|
export const AuthContext = createContext(false);
|
||||||
export const RefreshAuthContext = createContext<any>(null);
|
export const RefreshAuthContext = createContext(async () => false);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// States
|
// States
|
||||||
@ -123,9 +123,8 @@ function App() {
|
|||||||
<ToastContainer
|
<ToastContainer
|
||||||
position={"bottom-center"}
|
position={"bottom-center"}
|
||||||
theme={"dark"}
|
theme={"dark"}
|
||||||
stacked
|
|
||||||
newestOnTop
|
|
||||||
draggable
|
draggable
|
||||||
|
closeOnClick
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { apiRespBase, axiosInstance } from "./axiosInstance";
|
import { apiRespBaseType, axiosInstance } from "./axiosInstance";
|
||||||
import { authHealthCheckURL, authRefreshURL } from "./paths";
|
import { authHealthCheckURL, authRefreshURL } from "./paths";
|
||||||
|
|
||||||
export const apiAuthCheck = async (): Promise<
|
export const apiAuthCheck = async (): Promise<
|
||||||
AxiosResponse<apiRespBase, any>
|
AxiosResponse<apiRespBaseType, any>
|
||||||
> => {
|
> => {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get(authHealthCheckURL);
|
const response = await axiosInstance.get(authHealthCheckURL);
|
||||||
@ -14,7 +14,7 @@ export const apiAuthCheck = async (): Promise<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const apiAuthRefresh = async (): Promise<
|
export const apiAuthRefresh = async (): Promise<
|
||||||
AxiosResponse<apiRespBase, any>
|
AxiosResponse<apiRespBaseType, any>
|
||||||
> => {
|
> => {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get(authRefreshURL);
|
const response = await axiosInstance.get(authRefreshURL);
|
||||||
|
|||||||
@ -6,11 +6,11 @@ export const axiosInstance = axios.create({
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface apiRespBase {
|
export interface apiRespBaseType {
|
||||||
message?: string,
|
message?: string;
|
||||||
errors?: any[],
|
errors?: any[];
|
||||||
};
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { apiRespBase, axiosInstance } from "./axiosInstance";
|
import { apiRespBaseType, axiosInstance } from "./axiosInstance";
|
||||||
import { opFetchGraphURL } from "./paths";
|
import { opFetchGraphURL, opUpdateUserDataURL } from "./paths";
|
||||||
|
|
||||||
interface fetchGraphDataType extends apiRespBase {
|
interface fetchGraphDataType extends apiRespBaseType {
|
||||||
playlists?: {
|
playlists?: {
|
||||||
playlistID: string;
|
playlistID: string;
|
||||||
playlistName: string;
|
playlistName: string;
|
||||||
@ -13,6 +13,10 @@ interface fetchGraphDataType extends apiRespBase {
|
|||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface updateUserDataType extends apiRespBaseType {
|
||||||
|
removedLinks: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const apiFetchGraph = async (): Promise<
|
export const apiFetchGraph = async (): Promise<
|
||||||
AxiosResponse<fetchGraphDataType, any>
|
AxiosResponse<fetchGraphDataType, any>
|
||||||
> => {
|
> => {
|
||||||
@ -23,3 +27,14 @@ export const apiFetchGraph = async (): Promise<
|
|||||||
return error.response;
|
return error.response;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiUpdateUserData = async (): Promise<
|
||||||
|
AxiosResponse<updateUserDataType, any>
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.put(opUpdateUserDataURL);
|
||||||
|
return response;
|
||||||
|
} catch (error: any) {
|
||||||
|
return error.response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -7,3 +7,4 @@ export const authHealthCheckURL = "auth-health";
|
|||||||
export const authRefreshURL = "api/auth/refresh";
|
export const authRefreshURL = "api/auth/refresh";
|
||||||
|
|
||||||
export const opFetchGraphURL = "api/operations/fetch";
|
export const opFetchGraphURL = "api/operations/fetch";
|
||||||
|
export const opUpdateUserDataURL = "api/operations/update";
|
||||||
|
|||||||
55
src/components/APIWrapper/index.tsx
Normal file
55
src/components/APIWrapper/index.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||||
|
|
||||||
|
import { apiRespBaseType } from "../../api/axiosInstance";
|
||||||
|
import {
|
||||||
|
showErrorToastNotification,
|
||||||
|
showWarnToastNotification,
|
||||||
|
} from "../ToastNotification";
|
||||||
|
|
||||||
|
const maxRetries = 3;
|
||||||
|
|
||||||
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
// TODO: refreshAuth fn needs to be prop drilled (well, it's not really 'drilling', but still it's a single level)
|
||||||
|
// because hooks (namely, useContext) can't be used outside functional components
|
||||||
|
// so find a better way to pass refreshAuth
|
||||||
|
|
||||||
|
type APIWrapperProps<T extends apiRespBaseType> = {
|
||||||
|
apiFn(
|
||||||
|
data?: any,
|
||||||
|
config?: AxiosRequestConfig
|
||||||
|
): Promise<AxiosResponse<T, any>>;
|
||||||
|
refreshAuth: () => Promise<boolean>;
|
||||||
|
data?: any;
|
||||||
|
config?: AxiosRequestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
const APIWrapper = async <T extends apiRespBaseType>({
|
||||||
|
apiFn,
|
||||||
|
refreshAuth,
|
||||||
|
data,
|
||||||
|
config,
|
||||||
|
}: APIWrapperProps<T>) => {
|
||||||
|
for (let i = 1; i <= maxRetries + 1; i++) {
|
||||||
|
const apiResp = await apiFn(data, config);
|
||||||
|
|
||||||
|
if (apiResp === undefined) {
|
||||||
|
showErrorToastNotification("Please try again after sometime");
|
||||||
|
} else if (apiResp.status === 200) {
|
||||||
|
return apiResp;
|
||||||
|
} else if (apiResp.status === 401) {
|
||||||
|
showWarnToastNotification("Session expired, refreshing...");
|
||||||
|
if (!(await refreshAuth())) {
|
||||||
|
showErrorToastNotification("Session invalid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showErrorToastNotification(apiResp.data.message);
|
||||||
|
}
|
||||||
|
await sleep(i * i * 1000);
|
||||||
|
}
|
||||||
|
showErrorToastNotification("Please try again after sometime");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default APIWrapper;
|
||||||
@ -8,6 +8,6 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
box-shadow: 8px 8px var(--bg);
|
box-shadow: 4px 4px var(--bg);
|
||||||
background-color: var(--bgLinkInactive);
|
background-color: var(--bgLinkInactive);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,21 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: var(--mb-3);
|
gap: var(--mb-1);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 10vw;
|
width: 10vw;
|
||||||
padding: var(--mb-3);
|
padding: var(--mb-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operations_wrapper .icons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
margin: var(--mb-2) auto;
|
||||||
|
border-top: 1px solid white;
|
||||||
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import {
|
|||||||
type OnDelete,
|
type OnDelete,
|
||||||
type OnBeforeDelete,
|
type OnBeforeDelete,
|
||||||
} from "@xyflow/react";
|
} from "@xyflow/react";
|
||||||
import Dagre, { type GraphLabel } from "@dagrejs/dagre";
|
import Dagre from "@dagrejs/dagre";
|
||||||
|
|
||||||
import "@xyflow/react/dist/style.css";
|
import "@xyflow/react/dist/style.css";
|
||||||
import styles from "./Graph.module.css";
|
import styles from "./Graph.module.css";
|
||||||
@ -30,16 +30,19 @@ import styles from "./Graph.module.css";
|
|||||||
import { IoIosGitNetwork } from "react-icons/io";
|
import { IoIosGitNetwork } from "react-icons/io";
|
||||||
import { WiCloudRefresh } from "react-icons/wi";
|
import { WiCloudRefresh } from "react-icons/wi";
|
||||||
import { MdOutlineLock, MdOutlineLockOpen } from "react-icons/md";
|
import { MdOutlineLock, MdOutlineLockOpen } from "react-icons/md";
|
||||||
|
import { AiFillSpotify } from "react-icons/ai";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
showErrorToastNotification,
|
showErrorToastNotification,
|
||||||
showInfoToastNotification,
|
showInfoToastNotification,
|
||||||
|
showWarnToastNotification,
|
||||||
} from "../../components/ToastNotification";
|
} from "../../components/ToastNotification";
|
||||||
|
|
||||||
import { apiFetchGraph } from "../../api/operations";
|
import { apiFetchGraph, apiUpdateUserData } from "../../api/operations";
|
||||||
|
|
||||||
import { RefreshAuthContext } from "../../App";
|
import { RefreshAuthContext } from "../../App";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
|
import APIWrapper from "../../components/APIWrapper";
|
||||||
|
|
||||||
const initialNodes: Node[] = [];
|
const initialNodes: Node[] = [];
|
||||||
const initialEdges: Edge[] = [];
|
const initialEdges: Edge[] = [];
|
||||||
@ -67,11 +70,13 @@ const nodeOffsets = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Interactive {
|
type Interactive = {
|
||||||
ndDrag: boolean;
|
ndDrag: boolean;
|
||||||
ndConn: boolean;
|
ndConn: boolean;
|
||||||
elsSel: boolean;
|
elsSel: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type rankdirType = "TB" | "BT" | "LR" | "RL";
|
||||||
|
|
||||||
const initialInteractive: Interactive = {
|
const initialInteractive: Interactive = {
|
||||||
ndDrag: true,
|
ndDrag: true,
|
||||||
@ -147,12 +152,12 @@ const Graph = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
type getLayoutedElementsOpts = {
|
type getLayoutedElementsOpts = {
|
||||||
direction: GraphLabel["rankdir"];
|
direction: rankdirType;
|
||||||
};
|
};
|
||||||
const getLayoutedElements = (
|
const getLayoutedElements = (
|
||||||
nodes: Node[],
|
nodes: Node[],
|
||||||
edges: Edge[],
|
edges: Edge[],
|
||||||
options: getLayoutedElementsOpts = { direction: "TB" }
|
options: getLayoutedElementsOpts
|
||||||
) => {
|
) => {
|
||||||
const g = new Dagre.graphlib.Graph();
|
const g = new Dagre.graphlib.Graph();
|
||||||
g.setDefaultEdgeLabel(() => ({}));
|
g.setDefaultEdgeLabel(() => ({}));
|
||||||
@ -224,7 +229,7 @@ const Graph = () => {
|
|||||||
return finalLayout;
|
return finalLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
const arrangeLayout = (direction: GraphLabel["rankdir"]) => {
|
const arrangeLayout = (direction: rankdirType) => {
|
||||||
const layouted = getLayoutedElements(playlistNodes, linkEdges, {
|
const layouted = getLayoutedElements(playlistNodes, linkEdges, {
|
||||||
direction,
|
direction,
|
||||||
});
|
});
|
||||||
@ -237,18 +242,13 @@ const Graph = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchGraph = useCallback(async () => {
|
const fetchGraph = useCallback(async () => {
|
||||||
const resp = await apiFetchGraph();
|
const resp = await APIWrapper({ apiFn: apiFetchGraph, refreshAuth });
|
||||||
if (resp === undefined) {
|
|
||||||
showErrorToastNotification("Please try again after sometime");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (resp.status === 200) {
|
|
||||||
console.debug(
|
console.debug(
|
||||||
`graph fetched with ${resp.data.playlists?.length} nodes and ${resp.data.links?.length} edges`
|
`graph fetched with ${resp?.data.playlists?.length} nodes and ${resp?.data.links?.length} edges`
|
||||||
);
|
);
|
||||||
// place playlist nodes
|
// place playlist nodes
|
||||||
setPlaylistNodes(
|
setPlaylistNodes(
|
||||||
resp.data.playlists?.map((pl, idx) => {
|
resp?.data.playlists?.map((pl, idx) => {
|
||||||
return {
|
return {
|
||||||
id: `${pl.playlistID}`,
|
id: `${pl.playlistID}`,
|
||||||
position: {
|
position: {
|
||||||
@ -270,7 +270,7 @@ const Graph = () => {
|
|||||||
);
|
);
|
||||||
// connect links
|
// connect links
|
||||||
setLinkEdges(
|
setLinkEdges(
|
||||||
resp.data.links?.map((link, idx) => {
|
resp?.data.links?.map((link, idx) => {
|
||||||
return {
|
return {
|
||||||
id: `${link.from}->${link.to}`,
|
id: `${link.from}->${link.to}`,
|
||||||
source: link.from,
|
source: link.from,
|
||||||
@ -279,24 +279,22 @@ const Graph = () => {
|
|||||||
}) ?? []
|
}) ?? []
|
||||||
);
|
);
|
||||||
showInfoToastNotification("Graph updated.");
|
showInfoToastNotification("Graph updated.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (resp.status >= 500) {
|
|
||||||
showErrorToastNotification(resp.data.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (resp.status === 401) {
|
|
||||||
await refreshAuth();
|
|
||||||
}
|
|
||||||
showErrorToastNotification(resp.data.message);
|
|
||||||
return;
|
|
||||||
}, [refreshAuth]);
|
}, [refreshAuth]);
|
||||||
|
|
||||||
const onArrange = () => {
|
const updateUserData = async () => {
|
||||||
arrangeLayout("TB");
|
const resp = await APIWrapper({
|
||||||
|
apiFn: apiUpdateUserData,
|
||||||
|
refreshAuth,
|
||||||
|
});
|
||||||
|
showInfoToastNotification("Spotify synced.");
|
||||||
|
if (resp?.data.removedLinks)
|
||||||
|
showWarnToastNotification(
|
||||||
|
"Some links with deleted playlists were removed."
|
||||||
|
);
|
||||||
|
await refreshGraph();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRefresh = async () => {
|
const refreshGraph = async () => {
|
||||||
await fetchGraph();
|
await fetchGraph();
|
||||||
arrangeLayout("TB");
|
arrangeLayout("TB");
|
||||||
};
|
};
|
||||||
@ -304,19 +302,30 @@ const Graph = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchGraph();
|
fetchGraph();
|
||||||
// TODO: how to invoke async and sync fns in order correctly inside useEffect?
|
// TODO: how to invoke async and sync fns in order correctly inside useEffect?
|
||||||
// onRefresh();
|
// refreshGraph();
|
||||||
}, [fetchGraph]);
|
}, [fetchGraph]);
|
||||||
|
|
||||||
const toggleInteractive = () => {
|
const disableInteractive = () => {
|
||||||
setInteractive({
|
setInteractive({
|
||||||
ndDrag: !interactive.ndDrag,
|
ndDrag: false,
|
||||||
ndConn: !interactive.ndConn,
|
ndConn: false,
|
||||||
elsSel: !interactive.elsSel,
|
elsSel: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const enableInteractive = () => {
|
||||||
|
setInteractive({
|
||||||
|
ndDrag: true,
|
||||||
|
ndConn: true,
|
||||||
|
elsSel: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isInteractive = () => {
|
const isInteractive = () => {
|
||||||
return interactive.ndDrag && interactive.ndConn && interactive.elsSel;
|
return interactive.ndDrag || interactive.ndConn || interactive.elsSel;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleInteractive = () => {
|
||||||
|
isInteractive() ? disableInteractive() : enableInteractive();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -349,11 +358,11 @@ const Graph = () => {
|
|||||||
<Background variant={BackgroundVariant.Dots} gap={36} size={3} />
|
<Background variant={BackgroundVariant.Dots} gap={36} size={3} />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
<div className={styles.operations_wrapper}>
|
<div className={styles.operations_wrapper}>
|
||||||
<Button onClickMethod={onRefresh}>
|
<Button onClickMethod={refreshGraph}>
|
||||||
<WiCloudRefresh size={36} />
|
<WiCloudRefresh size={36} />
|
||||||
Refresh
|
Refresh Graph
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClickMethod={onArrange}>
|
<Button onClickMethod={() => arrangeLayout("TB")}>
|
||||||
<IoIosGitNetwork size={36} />
|
<IoIosGitNetwork size={36} />
|
||||||
Arrange
|
Arrange
|
||||||
</Button>
|
</Button>
|
||||||
@ -365,6 +374,14 @@ const Graph = () => {
|
|||||||
)}
|
)}
|
||||||
{isInteractive() ? "Lock" : "Unlock"}
|
{isInteractive() ? "Lock" : "Unlock"}
|
||||||
</Button>
|
</Button>
|
||||||
|
<hr className={styles.divider} />
|
||||||
|
<Button onClickMethod={updateUserData}>
|
||||||
|
<span className={styles.icons}>
|
||||||
|
<WiCloudRefresh size={36} />
|
||||||
|
<AiFillSpotify size={36} />
|
||||||
|
</span>
|
||||||
|
Sync Spotify
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user