Commit b736144a authored by SpinShare's avatar SpinShare

new user profile design

parent f923ad92
...@@ -1180,6 +1180,12 @@ ...@@ -1180,6 +1180,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/json-schema": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
...@@ -1405,6 +1411,24 @@ ...@@ -1405,6 +1411,24 @@
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true "dev": true
}, },
"ajv": {
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
},
"cacache": { "cacache": {
"version": "13.0.1", "version": "13.0.1",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
...@@ -1431,13 +1455,49 @@ ...@@ -1431,13 +1455,49 @@
"unique-filename": "^1.1.1" "unique-filename": "^1.1.1"
}, },
"dependencies": { "dependencies": {
"rimraf": { "fs-minipass": {
"version": "2.7.1", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.1.3" "minipass": "^3.0.0"
}
},
"minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-pipeline": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"p-map": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
"integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
} }
} }
} }
...@@ -1473,9 +1533,9 @@ ...@@ -1473,9 +1533,9 @@
} }
}, },
"make-dir": { "make-dir": {
"version": "3.0.2", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true, "dev": true,
"requires": { "requires": {
"semver": "^6.0.0" "semver": "^6.0.0"
...@@ -1520,6 +1580,15 @@ ...@@ -1520,6 +1580,15 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
...@@ -1543,20 +1612,63 @@ ...@@ -1543,20 +1612,63 @@
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "2.3.5", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
"integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==", "integrity": "sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^13.0.1", "cacache": "^13.0.1",
"find-cache-dir": "^3.2.0", "find-cache-dir": "^3.3.1",
"jest-worker": "^25.1.0", "jest-worker": "^25.4.0",
"p-limit": "^2.2.2", "p-limit": "^2.3.0",
"schema-utils": "^2.6.4", "schema-utils": "^2.6.6",
"serialize-javascript": "^2.1.2", "serialize-javascript": "^4.0.0",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"terser": "^4.4.3", "terser": "^4.6.12",
"webpack-sources": "^1.4.3" "webpack-sources": "^1.4.3"
},
"dependencies": {
"jest-worker": {
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz",
"integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
}
},
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
}
},
"serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
}
} }
} }
} }
...@@ -1890,9 +2002,9 @@ ...@@ -1890,9 +2002,9 @@
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg=="
}, },
"aggregate-error": { "aggregate-error": {
"version": "3.0.1", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true, "dev": true,
"requires": { "requires": {
"clean-stack": "^2.0.0", "clean-stack": "^2.0.0",
...@@ -2534,9 +2646,9 @@ ...@@ -2534,9 +2646,9 @@
} }
}, },
"bl": { "bl": {
"version": "1.2.2", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==",
"dev": true, "dev": true,
"requires": { "requires": {
"readable-stream": "^2.3.5", "readable-stream": "^2.3.5",
...@@ -3815,9 +3927,9 @@ ...@@ -3815,9 +3927,9 @@
}, },
"dependencies": { "dependencies": {
"dot-prop": { "dot-prop": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
"integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-obj": "^1.0.0" "is-obj": "^1.0.0"
...@@ -3941,9 +4053,9 @@ ...@@ -3941,9 +4053,9 @@
"dev": true "dev": true
}, },
"copy-webpack-plugin": { "copy-webpack-plugin": {
"version": "5.1.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz",
"integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^12.0.3", "cacache": "^12.0.3",
...@@ -3956,7 +4068,7 @@ ...@@ -3956,7 +4068,7 @@
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"p-limit": "^2.2.1", "p-limit": "^2.2.1",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"serialize-javascript": "^2.1.2", "serialize-javascript": "^4.0.0",
"webpack-log": "^2.0.0" "webpack-log": "^2.0.0"
}, },
"dependencies": { "dependencies": {
...@@ -4005,6 +4117,15 @@ ...@@ -4005,6 +4117,15 @@
"ajv-errors": "^1.0.0", "ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0" "ajv-keywords": "^3.1.0"
} }
},
"serialize-javascript": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
}
} }
} }
}, },
...@@ -6073,15 +6194,6 @@ ...@@ -6073,15 +6194,6 @@
"universalify": "^0.1.0" "universalify": "^0.1.0"
} }
}, },
"fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"fs-write-stream-atomic": { "fs-write-stream-atomic": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
...@@ -7857,33 +7969,6 @@ ...@@ -7857,33 +7969,6 @@
"integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==", "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==",
"dev": true "dev": true
}, },
"jest-worker": {
"version": "25.2.6",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.6.tgz",
"integrity": "sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
},
"dependencies": {
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"js-message": { "js-message": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
...@@ -8636,33 +8721,6 @@ ...@@ -8636,33 +8721,6 @@
} }
} }
}, },
"minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-pipeline": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz",
"integrity": "sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA==",
"dev": true,
"requires": {
"minipass": "^3.0.0"
}
},
"mississippi": { "mississippi": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
...@@ -9298,15 +9356,6 @@ ...@@ -9298,15 +9356,6 @@
"p-limit": "^1.1.0" "p-limit": "^1.1.0"
} }
}, },
"p-map": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
"integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
}
},
"p-retry": { "p-retry": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
...@@ -12976,6 +13025,11 @@ ...@@ -12976,6 +13025,11 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"vanilla-tilt": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/vanilla-tilt/-/vanilla-tilt-1.7.0.tgz",
"integrity": "sha512-u9yUhpSasFeqQCuiTon+RSb0aHzcj9stvWVXQIswzKL5oG491lkYk7U1GmhOAEZt7yPT6EiYZRJhIh2MFBncOA=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
...@@ -13371,6 +13425,14 @@ ...@@ -13371,6 +13425,14 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true "dev": true
}, },
"vue-tilt.js": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/vue-tilt.js/-/vue-tilt.js-1.1.1.tgz",
"integrity": "sha512-Tsc+01E9tO5TprUFplYEScU+DxQVY1sWiGlsqYb03R1fpSM5SWzBDeq1cb3Mj1vl+VbD/rlUM0vj5xW0DzUTRw==",
"requires": {
"vanilla-tilt": "^1.7.0"
}
},
"vuex": { "vuex": {
"version": "3.5.1", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
"vue-i18n": "^8.20.0", "vue-i18n": "^8.20.0",
"vue-observe-visibility": "^0.4.6", "vue-observe-visibility": "^0.4.6",
"vue-router": "^3.3.4", "vue-router": "^3.3.4",
"vue-tilt.js": "^1.1.1",
"vuex": "^3.5.1" "vuex": "^3.5.1"
}, },
"devDependencies": { "devDependencies": {
......
<template>
<div class="card-overlay">
<div class="close" v-on:click="close()"><i class="mdi mdi-close"></i></div>
<div class="overlay-content">
<img :src="card.icon" class="card-icon" v-tilt="{glare: true, 'max-glare': 0.8, reverse: true, max: 15}" />
<div class="card-title">{{ card.title }}</div>
<div class="card-description">{{ card.description }}</div>
<div class="card-given">{{ renderDate(card.givenDateFormat) }}</div>
<div class="button" v-on:click="close()">Close</div>
</div>
</div>
</template>
<script>
import { remote, ipcRenderer } from 'electron';
import { path } from 'path';
import moment from 'moment';
const { shell } = remote;
export default {
name: 'CardOverlay',
props: [
'card'
],
mounted: function() {
ipcRenderer.on("overlays-close", () => {
this.close();
});
},
methods: {
close() {
this.$parent.$emit('cardClose');
},
renderDate( date ) {
return moment( date ).format(this.$t('locale.dateFormat'));
}
}
}
</script>
<style scoped lang="less">
.card-overlay {
display: flex;
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
z-index: 1200;
background: rgba(0,0,0,0.6);
justify-content: center;
align-items: center;
& .close {
font-size: 32px;
color: #fff;
position: absolute;
top: 40px;
right: 40px;
cursor: pointer;
transition: 0.2s ease-in-out opacity;
&:hover {
opacity: 0.6;
}
}
& .overlay-content {
max-width: 320px;
text-align: center;
& img {
width: 200px;
height: 200px;
margin-bottom: 50px;
}
& .card-title {
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
}
& .card-description {
line-height: 1.5em;
opacity: 0.75;
margin-bottom: 10px;
}
& .card-given {
margin-bottom: 25px;
font-weight: bold;
}
& .button {
display: inline-block;
}
}
}
</style>
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
"userdetail.uploaded.header": "Uploaded Songs", "userdetail.uploaded.header": "Uploaded Songs",
"userdetail.uploaded.noresults": "This user did not upload any songs yet.", "userdetail.uploaded.noresults": "This user did not upload any songs yet.",
"userdetail.actions.reportButton": "Report", "userdetail.actions.reportButton": "Report",
"userdetail.charts.noresults": "This user has no charts yet.",
"userdetail.reviews.noresults": "This user has no reviews yet.",
"userdetail.spinplays.noresults": "This user did not release any SpinPlays yet.",
"settings.header": "Settings", "settings.header": "Settings",
"settings.client.header": "SpinShare", "settings.client.header": "SpinShare",
......
...@@ -4,10 +4,12 @@ import router from './router'; ...@@ -4,10 +4,12 @@ import router from './router';
import store from './store'; import store from './store';
import i18n from './i18n'; import i18n from './i18n';
import moment from 'moment' import moment from 'moment'
import VueTilt from 'vue-tilt.js'
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import VueObserveVisibility from 'vue-observe-visibility' import VueObserveVisibility from 'vue-observe-visibility'
import { VTooltip, VPopover, VClosePopover } from 'v-tooltip' import { VTooltip, VPopover, VClosePopover } from 'v-tooltip'
Vue.use(VueTilt);
Vue.use(VueObserveVisibility); Vue.use(VueObserveVisibility);
Vue.directive('tooltip', VTooltip); Vue.directive('tooltip', VTooltip);
......
...@@ -5,7 +5,7 @@ class SSAPI { ...@@ -5,7 +5,7 @@ class SSAPI {
if(process.env.NODE_ENV !== 'development') { if(process.env.NODE_ENV !== 'development') {
return false; return false;
} else { } else {
return false; return true;
} }
} }
...@@ -210,6 +210,54 @@ class SSAPI { ...@@ -210,6 +210,54 @@ class SSAPI {
}); });
} }
async getUserCharts(_userId) {
let apiPath = this.apiBase + "user/" + _userId + "/charts";
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
return response.data;
}).catch(function(error) {
throw new Error(error);
});
}
async getUserReviews(_userId) {
let apiPath = this.apiBase + "user/" + _userId + "/reviews";
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
return response.data;
}).catch(function(error) {
throw new Error(error);
});
}
async getUserSpinPlays(_userId) {
let apiPath = this.apiBase + "user/" + _userId + "/spinplays";
let supportedVersion = this.supportedVersion;
return axios.get(apiPath)
.then(function(response) {
if(response.data.version !== supportedVersion) {
throw new Error("Client is outdated!");
}
return response.data;
}).catch(function(error) {
throw new Error(error);
});
}
async searchAll() { async searchAll() {
let apiPath = this.apiBase + "searchAll"; let apiPath = this.apiBase + "searchAll";
let supportedVersion = this.supportedVersion; let supportedVersion = this.supportedVersion;
......
...@@ -13,6 +13,9 @@ import ViewSongDetail from '../views/SongDetail.vue'; ...@@ -13,6 +13,9 @@ import ViewSongDetail from '../views/SongDetail.vue';
import ViewSongDetailReviews from '../views/SongDetailReviews.vue'; import ViewSongDetailReviews from '../views/SongDetailReviews.vue';
import ViewSongDetailSpinPlays from '../views/SongDetailSpinPlays.vue'; import ViewSongDetailSpinPlays from '../views/SongDetailSpinPlays.vue';
import ViewUserDetail from '../views/UserDetail.vue'; import ViewUserDetail from '../views/UserDetail.vue';
import ViewUserDetailCharts from '../views/UserDetailCharts.vue';
import ViewUserDetailReviews from '../views/UserDetailReviews.vue';
import ViewUserDetailSpinPlays from '../views/UserDetailSpinPlays.vue';
import ViewSettings from '../views/Settings.vue'; import ViewSettings from '../views/Settings.vue';
import ViewTournament from '../views/Tournament.vue'; import ViewTournament from '../views/Tournament.vue';
import ViewError from '../views/Error.vue'; import ViewError from '../views/Error.vue';
...@@ -74,10 +77,33 @@ const routes = [{ ...@@ -74,10 +77,33 @@ const routes = [{
component: ViewSongDetailSpinPlays component: ViewSongDetailSpinPlays
} }
] ]
},, { }, {
path: '/user/:id', path: '/user/:id',
name: 'UserDetail', name: 'UserDetail',
component: ViewUserDetail component: ViewUserDetail,
redirect: {
name: 'UserDetailCharts'
},
children: [
{
alias: '',
path: 'charts',
name: 'UserDetailCharts',
component: ViewUserDetailCharts
},
{
alias: '',
path: 'reviews',
name: 'UserDetailReviews',
component: ViewUserDetailReviews
},
{
alias: '',
path: 'spinplays',
name: 'UserDetailSpinPlays',
component: ViewUserDetailSpinPlays
}
]
}, { }, {
path: '/settings', path: '/settings',
name: 'Settings', name: 'Settings',
......
<template> <template>
<section class="section-user-detail"> <section class="section-user-detail">
<div class="user-detail-background" :style="'background-image: url(' + avatar + '), url(' + require('@/assets/img/defaultAvatar.jpg') + ');'" v-if="apiFinished"> <header v-if="apiFinished">
<div class="user-detail-dim"> <div class="detail">
<div class="user-report" v-if="apiFinished"> <div class="user-avatar" :style="'background-image: url(' + avatar + '), url(' + require('@/assets/img/defaultAvatar.jpg') + ');'"></div>
<button class="button-report button" v-on:click="OpenReport">{{ $t('userdetail.actions.reportButton') }}</button> <div class="user-data">
</div> <div class="user-name">{{ username }} <i class="mdi mdi-patreon" v-if="isVerified"></i></div>
<div class="user-detail"> <div class="user-actions">
<div class="user-avatar" :style="'background-image: url(' + avatar + '), url(' + require('@/assets/img/defaultAvatar.jpg') + ');'"></div> <button class="button" v-on:click="OpenReport()">{{ $t('userdetail.actions.reportButton') }}</button>
<div class="user-meta-data">
<div class="user-name">{{ username }}</div>
<div class="user-badge user-badge-verified" v-if="isVerified">
<i class="mdi mdi-check-decagram"></i>
</div>
<div class="user-badge user-badge-patreon" v-if="isPatreon">
<i class="mdi mdi-patreon"></i>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<SongRow <div class="cards" v-if="cards.length > 0">
class="song-row-user" <div class="card" v-for="card in cards" :key="card.id" :style="'background-image: url(' + card.icon + ')'" v-on:click="OpenCardOverlay(card)"></div>
v-if="apiFinished && songs.length > 0"> </div>
<template v-slot:song-list>
<SongItem <div class="tabs">
v-for="song in songs" <router-link :to="{ name: 'UserDetailCharts', params: { id: id } }" class="tab" exact>Charts ({{ songs }})</router-link>
v-bind:key="song.id" <router-link :to="{ name: 'UserDetailReviews', params: { id: id } }" class="tab" exact>Reviews ({{ reviews }})</router-link>
v-bind="song" /> <router-link :to="{ name: 'UserDetailSpinPlays', params: { id: id } }" class="tab" exact>SpinPlays ({{ spinplays }})</router-link>
</template> </div>
</SongRow> </header>
<transition name="fade">
<CardOverlay v-if="showCardOverlay" v-bind:card="cardToShow" />
</transition>
<router-view v-if="apiFinished"></router-view>
<Loading v-if="!apiFinished" /> <Loading v-if="!apiFinished" />
</section> </section>
...@@ -36,19 +34,22 @@ ...@@ -36,19 +34,22 @@
<script> <script>
import { remote } from 'electron'; import { remote } from 'electron';
import moment from 'moment';
const { clipboard, shell } = remote; const { clipboard, shell } = remote;
import SSAPI from '@/modules/module.api.js'; import SSAPI from '@/modules/module.api.js';
import SongRow from '@/components/Song/SongRow.vue'; import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue'; import SongItem from '@/components/Song/SongItem.vue';
import Loading from '@/components/Loading.vue'; import Loading from '@/components/Loading.vue';
import CardOverlay from '@/components/Overlays/CardOverlay.vue';
export default { export default {
name: 'UserDetail', name: 'UserDetail',
components: { components: {
SongRow, SongRow,
SongItem, SongItem,
Loading Loading,
CardOverlay
}, },
data: function() { data: function() {
return { return {
...@@ -58,7 +59,12 @@ ...@@ -58,7 +59,12 @@
isVerified: false, isVerified: false,
isPatreon: false, isPatreon: false,
avatar: "", avatar: "",
songs: [] cards: [],
songs: 0,
reviews: 0,
spinplays: 0,
showCardOverlay: false,
cardToShow: null
} }
}, },
mounted: function() { mounted: function() {
...@@ -71,16 +77,28 @@ ...@@ -71,16 +77,28 @@
this.$data.isVerified = data.data.isVerified; this.$data.isVerified = data.data.isVerified;
this.$data.isPatreon = data.data.isPatreon; this.$data.isPatreon = data.data.isPatreon;
this.$data.avatar = data.data.avatar; this.$data.avatar = data.data.avatar;
this.$data.cards = data.data.cards;
this.$data.songs = data.data.songs; this.$data.songs = data.data.songs;
this.$data.reviews = data.data.reviews;
this.$data.spinplays = data.data.spinplays;
this.$data.apiFinished = true; this.$data.apiFinished = true;
} else { } else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } }); this.$router.push({ name: 'Error', params: { errorCode: data.status } });
} }
}); });
this.$on('cardClose', () => {
this.$data.showCardOverlay = false;
this.$data.cardToShow = null;
});
}, },
methods: { methods: {
OpenReport: function() { OpenReport: function() {
shell.openExternal("https://spinsha.re/report/user/" + this.$data.id); shell.openExternal("https://spinsha.re/report/user/" + this.$data.id);
},
OpenCardOverlay: function(cardToShow) {
this.$data.showCardOverlay = true;
this.$data.cardToShow = cardToShow;
} }
} }
} }
...@@ -88,80 +106,127 @@ ...@@ -88,80 +106,127 @@
<style scoped lang="less"> <style scoped lang="less">
.section-user-detail { .section-user-detail {
& .user-detail-background { & header {
background-size: cover; background: rgba(0,0,0,0.5);
background-position: center; padding: 25px 50px;
padding-bottom: 0px;
& .user-detail-dim { & .detail {
backdrop-filter: blur(10px); margin-bottom: 15px;
background: linear-gradient(180deg, rgba(0,0,0,0.4), #212629); display: grid;
grid-template-columns: 80px 1fr;
grid-gap: 15px;
& .user-detail { & .user-avatar {
padding: 50px; width: 80px;
display: grid; height: 80px;
grid-template-columns: 1fr; border-radius: 50%;
justify-items: center; background-position: center;
grid-gap: 25px; background-size: cover;
position: relative;
& .user-avatar { overflow: hidden;
width: 200px;
height: 200px; & .user-avatar-change {
align-self: center; position: absolute;
background: #eee; top: 0px;
border-radius: 50%; left: 0px;
background-size: cover; bottom: 0px;
background-position: center; right: 0px;
} background: rgba(0,0,0,0.6);
& .user-meta-data {
display: flex; display: flex;
height: 48px; justify-content: center;
align-items: center; align-items: center;
opacity: 0;
& .user-name { transition: 0.2s ease opacity;
font-weight: bold; cursor: pointer;
font-size: 48px;
& .mdi {
font-size: 28px;
}
& input {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
cursor: pointer;
opacity: 0;
} }
& .user-badge { }
display: flex; &:hover {
align-items: center; & .user-avatar-change {
justify-content: center; opacity: 1;
font-size: 32px;
margin-left: 20px;
} }
} }
} }
}
}
& .user-report {
position: absolute;
top: 25px;
right: 25px;
& .button { & .user-data {
padding: 15px 30px; display: grid;
font-size: 16px; grid-template-rows: 1fr 40px;
transition: 0.2s ease-in-out all;
&.button-primary { & .user-name {
background: #fff; font-size: 22px;
color: #222; margin-bottom: 22px;
}
& .user-actions {
& .button {
margin-right: 7px;
}
}
}
}
& .cards {
margin-bottom: 25px;
display: flex;
& .card {
width: 100px;
height: 100px;
margin-right: 10px;
background-position: center;
background-size: cover;
opacity: 1;
transition: 0.2s ease-in-out opacity;
&:hover { &:hover {
background: #fff; opacity: 0.6;
color: #222; cursor: pointer;
} }
} }
}
& .tabs {
display: flex;
&:hover { & .tab {
color: #fff; font-size: 14px;
opacity: 0.6; font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.25em;
padding: 15px 40px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
background: rgba(0,0,0,0.6);
color: rgba(255,255,255,0.4);
transition: 0.2s ease-in-out all;
text-decoration: none;
&:not(.active):hover {
cursor: pointer;
background: #272c2e;
color: rgba(255,255,255,0.75);
}
&.router-link-active {
opacity: 1;
color: rgba(255,255,255,1);
background: #212629;
}
} }
} }
} }
& .song-row-user { & .song-row-user {
display: grid;
padding: 50px; padding: 50px;
display: grid;
} }
} }
</style> </style>
\ No newline at end of file
<template>
<section class="section-userdetail-charts">
<SongRow
v-if="apiFinished && charts.length > 0">
<template v-slot:song-list>
<SongItem
v-for="song in charts"
v-bind:key="song.id"
v-bind="song" />
</template>
</SongRow>
<div class="song-list-noresults" v-if="apiFinished && charts.length < 1">
<div class="noresults-text">{{ this.$t('userdetail.charts.noresults') }}</div>
</div>
<Loading v-if="!apiFinished" style="padding: 50px 0px;" />
</section>
</template>
<script>
import { remote } from 'electron';
const { clipboard, shell } = remote;
import SSAPI from '@/modules/module.api.js';
import SongRow from '@/components/Song/SongRow.vue';
import SongItem from '@/components/Song/SongItem.vue';
import Loading from '@/components/Loading.vue';
export default {
name: 'UserDetailCharts',
components: {
SongRow,
SongItem,
Loading,
},
data: function() {
return {
apiFinished: false,
charts: [],
}
},
mounted: function() {
let ssapi = new SSAPI();
ssapi.getUserCharts(this.$route.params.id).then((data) => {
if(data.status == 200) {
this.$data.charts = data.data;
this.$data.apiFinished = true;
} else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } });
}
});
},
methods: {
}
}
</script>
<style scoped lang="less">
.section-userdetail-charts {
padding: 50px;
& .song-list-noresults {
display: block;
background: rgba(255,255,255,0.1);
border-radius: 6px;
padding: 25px;
opacity: 0.6;
text-align: center;
}
}
</style>
\ No newline at end of file
<template>
<section class="section-userdetail-reviews">
<div class="reviews" v-if="apiFinished">
<div class="review-list" v-if="reviews.length > 0">
<router-link :to="{ name: 'SongDetail', params: { id: review.song.id } }" class="review" v-for="review in reviews" :key="review.id">
<div class="metadata">
<div class="avatar" :style="'background-image: url(' + review.user.coverReference + '), url(' + require('@/assets/img/defaultAvatar.jpg') + ');'"></div>
<div class="text">
<div class="username">{{ review.user.username }}</div>
<div class="subline">
<i :class="review.recommended ? 'mdi mdi-thumb-up positive' : 'mdi mdi-thumb-down negative'"></i>
<span>{{ renderDate(review.reviewDate.date ) }}</span>
</div>
</div>
</div>
<div class="comment" v-if="review.comment != ''">{{ review.comment }}</div>
</router-link>
</div>
<div class="song-list-noresults" v-if="apiFinished && reviews.length < 1">
<div class="noresults-text">{{ this.$t('userdetail.reviews.noresults') }}</div>
</div>
</div>
<Loading v-if="!apiFinished" style="padding: 50px 0px;" />
</section>
</template>
<script>
import { remote } from 'electron';
import moment from 'moment';
const { clipboard, shell } = remote;
import SSAPI from '@/modules/module.api.js';
import Loading from '@/components/Loading.vue';
export default {
name: 'UserDetailReviews',
components: {
Loading,
},
data: function() {
return {
apiFinished: false,
reviews: [],
}
},
mounted: function() {
let ssapi = new SSAPI();
ssapi.getUserReviews(this.$route.params.id).then((data) => {
if(data.status == 200) {
console.log(data.data);
this.$data.reviews = data.data;
this.$data.apiFinished = true;
} else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } });
}
});
},
methods: {
renderDate( date ) {
return moment( date ).format(this.$t('locale.dateFormat'));
}
}
}
</script>
<style scoped lang="less">
.section-userdetail-reviews {
padding: 50px;
& .review-list {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 25px;
& .review {
background: rgba(255,255,255,0.1);
border-radius: 4px;
padding: 20px;
display: block;
transition: 0.2s ease-in-out opacity;
& .metadata {
display: grid;
grid-template-columns: auto 1fr auto;
grid-gap: 15px;
& .avatar {
width: 48px;
height: 48px;
border-radius: 48px;
background-position: center;
background-size: cover;
display: block;
color: #fff;
text-decoration: none;
transition: 0.2s ease-in-out opacity;
&:hover {
opacity: 0.6;
}
}
& .text {
& .username {
font-size: 16px;
font-weight: bold;
margin-bottom: 5px;
display: block;
color: #fff;
text-decoration: none;
}
& .subline {
display: grid;
grid-gap: 5px;
grid-template-columns: auto 1fr;
align-items: center;
color: #fff;
text-decoration: none;
& i.positive { color: #62d38a; }
& i.negative { color: #f73c56; }
& span {
opacity: 0.6;
}
}
}
}
& .comment {
margin-top: 15px;
line-height: 1.5em;
word-break: normal;
color: #fff;
text-decoration: none;
}
&:hover {
opacity: 0.4;
}
}
}
& .song-list-noresults {
display: block;
background: rgba(255,255,255,0.1);
border-radius: 6px;
padding: 25px;
opacity: 0.6;
text-align: center;
}
}
</style>
\ No newline at end of file
<template>
<section class="section-userdetail-spinplays">
<div class="spinplays" v-if="apiFinished">
<div class="video-list" v-if="spinplays.length > 0">
<div class="spinplay" v-for="spinplay in spinplays" :key="spinplay.id">
<div v-on:click="OpenExternal(spinplay.videoUrl)" class="thumbnail" :style="'background-image: url(' + spinplay.videoThumbnail + ');'"></div>
</div>
</div>
<div class="song-list-noresults" v-if="apiFinished && spinplays.length < 1">
<div class="noresults-text">{{ this.$t('userdetail.spinplays.noresults') }}</div>
</div>
</div>
<Loading v-if="!apiFinished" style="padding: 50px 0px;" />
</section>
</template>
<script>
import { remote } from 'electron';
const { clipboard, shell } = remote;
import SSAPI from '@/modules/module.api.js';
import Loading from '@/components/Loading.vue';
export default {
name: 'UserDetailSpinPlays',
components: {
Loading,
},
data: function() {
return {
apiFinished: false,
spinplays: [],
}
},
mounted: function() {
let ssapi = new SSAPI();
ssapi.getUserSpinPlays(this.$route.params.id).then((data) => {
if(data.status == 200) {
this.$data.spinplays = data.data;
this.$data.apiFinished = true;
} else {
this.$router.push({ name: 'Error', params: { errorCode: data.status } });
}
});
},
methods: {
OpenExternal( url ) {
shell.openExternal(url);
}
}
}
</script>
<style scoped lang="less">
.section-userdetail-spinplays {
padding: 50px;
& .spinplays {
& .video-list {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 15px;
& .spinplay {
background: rgba(255,255,255,0.1);
border-radius: 4px;
overflow: hidden;
color: #fff;
text-decoration: none;
& .thumbnail {
width: 100%;
display: block;
padding-top: 56.25%;
background: rgba(255,255,255,0.1);
background-position: center;
background-size: cover;
transition: 0.2s ease-in-out opacity;
&:hover {
opacity: 0.6;
cursor: pointer;
}
}
}
}
}
& .song-list-noresults {
display: block;
background: rgba(255,255,255,0.1);
border-radius: 6px;
padding: 25px;
opacity: 0.6;
text-align: center;
}
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment