Merge branch 'master' into dependabot/npm_and_yarn/backend/babel/preset-env-7.22.9
2
.github/file-filters.yml
vendored
@ -1,4 +1,5 @@
|
||||
backend: &backend
|
||||
- '.github/workflows/test-backend.yml'
|
||||
- 'backend/**/*'
|
||||
- 'neo4j/**/*'
|
||||
|
||||
@ -6,4 +7,5 @@ docker: &docker
|
||||
- 'docker-compose.*'
|
||||
|
||||
webapp: &webapp
|
||||
- '.github/workflows/test-webapp.yml'
|
||||
- 'webapp/**/*'
|
||||
|
||||
42
.github/workflows/cleanup-cache-at-pr-closing.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
###############################################################################
|
||||
# A Github repo has max 10 GB of cache.
|
||||
# https://github.blog/changelog/2021-11-23-github-actions-cache-size-is-now-increased-to-10gb-per-repository/
|
||||
#
|
||||
# To avoid "cache thrashing" by their cache eviction policy it is recommended
|
||||
# to apply a cache cleanup workflow at PR closing to dele cache leftovers of
|
||||
# the current branch:
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
|
||||
###############################################################################
|
||||
|
||||
name: ocelot.social cache cleanup on pr closing
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
clean-branch-cache:
|
||||
name: Cleanup branch cache
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
74
.github/workflows/test-backend.yml
vendored
@ -1,7 +1,7 @@
|
||||
name: ocelot.social backend test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
- name: Check for frontend file changes
|
||||
- name: Check for backend file changes
|
||||
uses: dorny/paths-filter@v2.11.1
|
||||
id: changes
|
||||
with:
|
||||
@ -34,12 +34,13 @@ jobs:
|
||||
run: |
|
||||
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
|
||||
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache-neo4j
|
||||
uses: actions/cache/save@v3.3.1
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-backend-neo4j-cache
|
||||
|
||||
build_test_backend:
|
||||
name: Docker Build Test - Backend
|
||||
@ -54,12 +55,13 @@ jobs:
|
||||
run: |
|
||||
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
|
||||
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache-backend
|
||||
uses: actions/cache/save@v3.3.1
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
|
||||
lint_backend:
|
||||
name: Lint Backend
|
||||
@ -84,28 +86,29 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image (Neo4J)
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Restore Neo4J cache
|
||||
uses: actions/cache/restore@v3.3.1
|
||||
with:
|
||||
name: docker-neo4j-image
|
||||
path: /tmp
|
||||
path: /tmp/neo4j.tar
|
||||
key: ${{ github.run_id }}-backend-neo4j-cache
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/neo4j.tar
|
||||
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Restore Backend cache
|
||||
uses: actions/cache/restore@v3.3.1
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
path: /tmp/backend.tar
|
||||
key: ${{ github.run_id }}-backend-cache
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
- name: Load Docker Images
|
||||
run: |
|
||||
docker load < /tmp/neo4j.tar
|
||||
docker load < /tmp/backend.tar
|
||||
|
||||
- name: backend | copy env files webapp
|
||||
run: cp webapp/.env.template webapp/.env
|
||||
- name: backend | copy env files backend
|
||||
run: cp backend/.env.template backend/.env
|
||||
- name: backend | copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp backend/.env.template backend/.env
|
||||
|
||||
- name: backend | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
|
||||
@ -118,3 +121,20 @@ jobs:
|
||||
|
||||
- name: backend | Unit test incl. coverage check
|
||||
run: docker-compose exec -T backend yarn test
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
if: ${{ needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.docker == 'true' }}
|
||||
needs: [files-changed, unit_test_backend]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-backend-neo4j-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
KEY="${{ github.run_id }}-backend-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
|
||||
102
.github/workflows/test-e2e.yml
vendored
@ -1,9 +1,54 @@
|
||||
name: ocelot.social end-to-end test CI
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
docker_preparation:
|
||||
name: Fullstack test preparation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp backend/.env.template backend/.env
|
||||
|
||||
- name: Build docker images
|
||||
run: |
|
||||
mkdir /tmp/images
|
||||
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
|
||||
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/images/neo4j.tar
|
||||
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
|
||||
docker save "ocelotsocialnetwork/backend:test" > /tmp/images/backend.tar
|
||||
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
|
||||
docker save "ocelotsocialnetwork/webapp:test" > /tmp/images/webapp.tar
|
||||
|
||||
- name: Install cypress requirements
|
||||
run: |
|
||||
wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
|
||||
cd backend
|
||||
yarn install
|
||||
yarn build
|
||||
cd ..
|
||||
yarn install
|
||||
|
||||
- name: Cache docker images
|
||||
id: cache
|
||||
uses: actions/cache/save@v3.3.1
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
/home/runner/.cache/Cypress
|
||||
/home/runner/work/Ocelot-Social/Ocelot-Social
|
||||
/tmp/images/
|
||||
key: ${{ github.run_id }}-e2e-preparation-cache
|
||||
|
||||
fullstack_tests:
|
||||
name: Fullstack tests
|
||||
if: success()
|
||||
needs: docker_preparation
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
jobs: 8
|
||||
@ -12,28 +57,27 @@ jobs:
|
||||
# run copies of the current job in parallel
|
||||
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Restore cache
|
||||
uses: actions/cache/restore@v3.3.1
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
/opt/cucumber-json-formatter
|
||||
/home/runner/.cache/Cypress
|
||||
/home/runner/work/Ocelot-Social/Ocelot-Social
|
||||
/tmp/images/
|
||||
key: ${{ github.run_id }}-e2e-preparation-cache
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: webapp | copy env file
|
||||
run: cp webapp/.env.template webapp/.env
|
||||
|
||||
- name: backend | copy env file
|
||||
run: cp backend/.env.template backend/.env
|
||||
|
||||
- name: boot up test system | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
|
||||
|
||||
- name: Full stack tests | prepare
|
||||
- name: Boot up test system | docker-compose
|
||||
run: |
|
||||
wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
|
||||
chmod +x /opt/cucumber-json-formatter
|
||||
sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter
|
||||
cd backend
|
||||
yarn install
|
||||
yarn build
|
||||
cd ..
|
||||
yarn install
|
||||
docker load < /tmp/images/neo4j.tar
|
||||
docker load < /tmp/images/backend.tar
|
||||
docker load < /tmp/images/webapp.tar
|
||||
docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
|
||||
sleep 90s
|
||||
|
||||
- name: Full stack tests | run tests
|
||||
id: e2e-tests
|
||||
@ -44,17 +88,25 @@ jobs:
|
||||
run: |
|
||||
cd cypress/
|
||||
node create-cucumber-html-report.js
|
||||
|
||||
- name: End-to-end tests | if tests failed, get pr number
|
||||
id: pr
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
uses: 8BitJonny/gh-get-current-pr@2.2.0
|
||||
|
||||
- name: End-to-end tests | if tests failed, upload report
|
||||
- name: Full stack tests | if tests failed, upload report
|
||||
id: e2e-report
|
||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ocelot-e2e-test-report-pr${{ steps.pr.outputs.number }}
|
||||
name: ocelot-e2e-test-report-pr${{ needs.docker_preparation.outputs.pr-number }}
|
||||
path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
needs: [docker_preparation, fullstack_tests]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-e2e-preparation-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
44
.github/workflows/test-webapp.yml
vendored
@ -1,7 +1,7 @@
|
||||
name: ocelot.social webapp test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
@ -34,7 +34,7 @@ jobs:
|
||||
run: |
|
||||
scripts/translations/sort.sh
|
||||
scripts/translations/missing-keys.sh
|
||||
|
||||
|
||||
build_test_webapp:
|
||||
name: Docker Build Test - Webapp
|
||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true'
|
||||
@ -44,16 +44,16 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: webapp | Build 'test' image
|
||||
- name: Webapp | Build 'test' image
|
||||
run: |
|
||||
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
|
||||
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
- name: Cache docker image
|
||||
uses: actions/cache/save@v3.3.1
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
|
||||
lint_webapp:
|
||||
name: Lint Webapp
|
||||
@ -78,20 +78,19 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image (Webapp)
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Restore webapp cache
|
||||
uses: actions/cache/restore@v3.3.1
|
||||
with:
|
||||
name: docker-webapp-test
|
||||
path: /tmp
|
||||
path: /tmp/webapp.tar
|
||||
key: ${{ github.run_id }}-webapp-cache
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/webapp.tar
|
||||
|
||||
- name: backend | copy env files webapp
|
||||
run: cp webapp/.env.template webapp/.env
|
||||
|
||||
- name: backend | copy env files backend
|
||||
run: cp backend/.env.template backend/.env
|
||||
- name: Copy env files
|
||||
run: |
|
||||
cp webapp/.env.template webapp/.env
|
||||
cp backend/.env.template backend/.env
|
||||
|
||||
- name: backend | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
||||
@ -99,3 +98,18 @@ jobs:
|
||||
- name: webapp | Unit tests incl. coverage check
|
||||
run: docker-compose exec -T webapp yarn test
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
if: ${{ needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.webapp == 'true' }}
|
||||
needs: [files-changed, unit_test_webapp]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Delete cache
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
KEY="${{ github.run_id }}-webapp-cache"
|
||||
gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm
|
||||
|
||||
|
||||
@ -6,6 +6,10 @@ export const createMessageMutation = () => {
|
||||
CreateMessage(roomId: $roomId, content: $content) {
|
||||
id
|
||||
content
|
||||
senderId
|
||||
username
|
||||
avatar
|
||||
date
|
||||
saved
|
||||
distributed
|
||||
seen
|
||||
@ -17,12 +21,15 @@ export const createMessageMutation = () => {
|
||||
export const messageQuery = () => {
|
||||
return gql`
|
||||
query ($roomId: ID!, $first: Int, $offset: Int) {
|
||||
Message(roomId: $roomId, first: $first, offset: $offset, orderBy: createdAt_desc) {
|
||||
Message(roomId: $roomId, first: $first, offset: $offset, orderBy: indexId_desc) {
|
||||
_id
|
||||
id
|
||||
indexId
|
||||
content
|
||||
senderId
|
||||
author {
|
||||
id
|
||||
}
|
||||
username
|
||||
avatar
|
||||
date
|
||||
|
||||
@ -6,18 +6,10 @@ export const createRoomMutation = () => {
|
||||
CreateRoom(userId: $userId) {
|
||||
id
|
||||
roomId
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const roomQuery = () => {
|
||||
return gql`
|
||||
query {
|
||||
Room {
|
||||
id
|
||||
roomId
|
||||
roomName
|
||||
lastMessageAt
|
||||
unreadCount
|
||||
#avatar
|
||||
users {
|
||||
_id
|
||||
id
|
||||
@ -30,3 +22,46 @@ export const roomQuery = () => {
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const roomQuery = () => {
|
||||
return gql`
|
||||
query Room($first: Int, $offset: Int, $id: ID) {
|
||||
Room(first: $first, offset: $offset, id: $id, orderBy: lastMessageAt_desc) {
|
||||
id
|
||||
roomId
|
||||
roomName
|
||||
avatar
|
||||
lastMessageAt
|
||||
unreadCount
|
||||
lastMessage {
|
||||
_id
|
||||
id
|
||||
content
|
||||
senderId
|
||||
username
|
||||
avatar
|
||||
date
|
||||
saved
|
||||
distributed
|
||||
seen
|
||||
}
|
||||
users {
|
||||
_id
|
||||
id
|
||||
name
|
||||
avatar {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const unreadRoomsQuery = () => {
|
||||
return gql`
|
||||
query {
|
||||
UnreadRooms
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
@ -9,10 +9,9 @@ function walkRecursive(data, fields, fieldName, callback, _key?) {
|
||||
if (!Array.isArray(fields)) {
|
||||
throw new Error('please provide an fields array for the walkRecursive helper')
|
||||
}
|
||||
if (data && typeof data === 'string' && fields.includes(_key)) {
|
||||
// well we found what we searched for, lets replace the value with our callback result
|
||||
const key = _key.split('!')
|
||||
if (key.length === 1 || key[1] !== fieldName) data = callback(data, key[0])
|
||||
const fieldDef = fields.find((f) => f.field === _key)
|
||||
if (data && typeof data === 'string' && fieldDef) {
|
||||
if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key)
|
||||
} else if (data && Array.isArray(data)) {
|
||||
// go into the rabbit hole and dig through that array
|
||||
data.forEach((res, index) => {
|
||||
|
||||
60
backend/src/middleware/chatMiddleware.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { isArray } from 'lodash'
|
||||
|
||||
const setRoomProps = (room) => {
|
||||
if (room.users) {
|
||||
room.users.forEach((user) => {
|
||||
user._id = user.id
|
||||
})
|
||||
}
|
||||
if (room.lastMessage) {
|
||||
room.lastMessage._id = room.lastMessage.id
|
||||
}
|
||||
}
|
||||
|
||||
const setMessageProps = (message, context) => {
|
||||
message._id = message.id
|
||||
if (message.senderId !== context.user.id) {
|
||||
message.distributed = true
|
||||
}
|
||||
}
|
||||
|
||||
const roomProperties = async (resolve, root, args, context, info) => {
|
||||
const resolved = await resolve(root, args, context, info)
|
||||
if (resolved) {
|
||||
if (isArray(resolved)) {
|
||||
resolved.forEach((room) => {
|
||||
setRoomProps(room)
|
||||
})
|
||||
} else {
|
||||
setRoomProps(resolved)
|
||||
}
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
const messageProperties = async (resolve, root, args, context, info) => {
|
||||
const resolved = await resolve(root, args, context, info)
|
||||
if (resolved) {
|
||||
if (isArray(resolved)) {
|
||||
resolved.forEach((message) => {
|
||||
setMessageProps(message, context)
|
||||
})
|
||||
} else {
|
||||
setMessageProps(resolved, context)
|
||||
}
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
Room: roomProperties,
|
||||
Message: messageProperties,
|
||||
},
|
||||
Mutation: {
|
||||
CreateRoom: roomProperties,
|
||||
},
|
||||
Subscription: {
|
||||
chatMessageAdded: messageProperties,
|
||||
},
|
||||
}
|
||||
@ -30,6 +30,7 @@ const standardSanitizeHtmlOptions = {
|
||||
'strike',
|
||||
'span',
|
||||
'blockquote',
|
||||
'usertag',
|
||||
],
|
||||
allowedAttributes: {
|
||||
a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
|
||||
|
||||
@ -14,6 +14,7 @@ import login from './login/loginMiddleware'
|
||||
import sentry from './sentryMiddleware'
|
||||
import languages from './languages/languages'
|
||||
import userInteractions from './userInteractions'
|
||||
import chatMiddleware from './chatMiddleware'
|
||||
|
||||
export default (schema) => {
|
||||
const middlewares = {
|
||||
@ -31,6 +32,7 @@ export default (schema) => {
|
||||
orderBy,
|
||||
languages,
|
||||
userInteractions,
|
||||
chatMiddleware,
|
||||
}
|
||||
|
||||
let order = [
|
||||
@ -49,6 +51,7 @@ export default (schema) => {
|
||||
'softDelete',
|
||||
'includedFields',
|
||||
'orderBy',
|
||||
'chatMiddleware',
|
||||
]
|
||||
|
||||
// add permisions middleware at the first position (unless we're seeding)
|
||||
|
||||
@ -408,6 +408,7 @@ export default shield(
|
||||
getInviteCode: isAuthenticated, // and inviteRegistration
|
||||
Room: isAuthenticated,
|
||||
Message: isAuthenticated,
|
||||
UnreadRooms: isAuthenticated,
|
||||
},
|
||||
Mutation: {
|
||||
'*': deny,
|
||||
|
||||
@ -3,11 +3,11 @@ import { cleanHtml } from '../middleware/helpers/cleanHtml'
|
||||
|
||||
// exclamation mark separetes field names, that should not be sanitized
|
||||
const fields = [
|
||||
'content',
|
||||
'contentExcerpt',
|
||||
'reasonDescription',
|
||||
'description!embed',
|
||||
'descriptionExcerpt',
|
||||
{ field: 'content', excludes: ['CreateMessage', 'Message'] },
|
||||
{ field: 'contentExcerpt' },
|
||||
{ field: 'reasonDescription' },
|
||||
{ field: 'description', excludes: ['embed'] },
|
||||
{ field: 'descriptionExcerpt' },
|
||||
]
|
||||
|
||||
export default {
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory, { cleanDatabase } from '../../db/factories'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import { createRoomMutation } from '../../graphql/rooms'
|
||||
import { createRoomMutation, roomQuery } from '../../graphql/rooms'
|
||||
import { createMessageMutation, messageQuery, markMessagesAsSeen } from '../../graphql/messages'
|
||||
import createServer from '../../server'
|
||||
import createServer, { pubsub } from '../../server'
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
const pubsubSpy = jest.spyOn(pubsub, 'publish')
|
||||
|
||||
let query
|
||||
let mutate
|
||||
let authenticatedUser
|
||||
@ -22,6 +24,9 @@ beforeAll(async () => {
|
||||
driver,
|
||||
neode,
|
||||
user: authenticatedUser,
|
||||
cypherParams: {
|
||||
currentUserId: authenticatedUser ? authenticatedUser.id : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -55,6 +60,10 @@ describe('Message', () => {
|
||||
})
|
||||
|
||||
describe('create message', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
await expect(
|
||||
@ -77,7 +86,7 @@ describe('Message', () => {
|
||||
})
|
||||
|
||||
describe('room does not exist', () => {
|
||||
it('returns null', async () => {
|
||||
it('returns null and does not publish subscription', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createMessageMutation(),
|
||||
@ -92,6 +101,7 @@ describe('Message', () => {
|
||||
CreateMessage: null,
|
||||
},
|
||||
})
|
||||
expect(pubsubSpy).not.toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -107,7 +117,7 @@ describe('Message', () => {
|
||||
})
|
||||
|
||||
describe('user chats in room', () => {
|
||||
it('returns the message', async () => {
|
||||
it('returns the message and publishes subscriptions', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createMessageMutation(),
|
||||
@ -122,12 +132,92 @@ describe('Message', () => {
|
||||
CreateMessage: {
|
||||
id: expect.any(String),
|
||||
content: 'Some nice message to other chatting user',
|
||||
senderId: 'chatting-user',
|
||||
username: 'Chatting User',
|
||||
avatar: expect.any(String),
|
||||
date: expect.any(String),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(pubsubSpy).toBeCalledWith('ROOM_COUNT_UPDATED', {
|
||||
roomCountUpdated: '1',
|
||||
userId: 'other-chatting-user',
|
||||
})
|
||||
expect(pubsubSpy).toBeCalledWith('CHAT_MESSAGE_ADDED', {
|
||||
chatMessageAdded: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
content: 'Some nice message to other chatting user',
|
||||
senderId: 'chatting-user',
|
||||
username: 'Chatting User',
|
||||
avatar: expect.any(String),
|
||||
date: expect.any(String),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false,
|
||||
}),
|
||||
userId: 'other-chatting-user',
|
||||
})
|
||||
})
|
||||
|
||||
describe('room is updated as well', () => {
|
||||
it('has last message set', async () => {
|
||||
const result = await query({ query: roomQuery() })
|
||||
await expect(result).toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: [
|
||||
expect.objectContaining({
|
||||
lastMessageAt: expect.any(String),
|
||||
unreadCount: 0,
|
||||
lastMessage: expect.objectContaining({
|
||||
_id: result.data.Room[0].lastMessage.id,
|
||||
id: expect.any(String),
|
||||
content: 'Some nice message to other chatting user',
|
||||
senderId: 'chatting-user',
|
||||
username: 'Chatting User',
|
||||
avatar: expect.any(String),
|
||||
date: expect.any(String),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('unread count for other user', () => {
|
||||
it('has unread count = 1', async () => {
|
||||
authenticatedUser = await otherChattingUser.toJson()
|
||||
await expect(query({ query: roomQuery() })).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: [
|
||||
expect.objectContaining({
|
||||
lastMessageAt: expect.any(String),
|
||||
unreadCount: 1,
|
||||
lastMessage: expect.objectContaining({
|
||||
_id: expect.any(String),
|
||||
id: expect.any(String),
|
||||
content: 'Some nice message to other chatting user',
|
||||
senderId: 'chatting-user',
|
||||
username: 'Chatting User',
|
||||
avatar: expect.any(String),
|
||||
date: expect.any(String),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,7 +1,36 @@
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import Resolver from './helpers/Resolver'
|
||||
|
||||
import { getUnreadRoomsCount } from './rooms'
|
||||
import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '../../server'
|
||||
import { withFilter } from 'graphql-subscriptions'
|
||||
|
||||
const setMessagesAsDistributed = async (undistributedMessagesIds, session) => {
|
||||
return session.writeTransaction(async (transaction) => {
|
||||
const setDistributedCypher = `
|
||||
MATCH (m:Message) WHERE m.id IN $undistributedMessagesIds
|
||||
SET m.distributed = true
|
||||
RETURN m { .* }
|
||||
`
|
||||
const setDistributedTxResponse = await transaction.run(setDistributedCypher, {
|
||||
undistributedMessagesIds,
|
||||
})
|
||||
const messages = await setDistributedTxResponse.records.map((record) => record.get('m'))
|
||||
return messages
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
Subscription: {
|
||||
chatMessageAdded: {
|
||||
subscribe: withFilter(
|
||||
() => pubsub.asyncIterator(CHAT_MESSAGE_ADDED),
|
||||
(payload, variables, context) => {
|
||||
return payload.userId === context.user?.id
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
Query: {
|
||||
Message: async (object, params, context, resolveInfo) => {
|
||||
const { roomId } = params
|
||||
@ -20,33 +49,15 @@ export default {
|
||||
const undistributedMessagesIds = resolved
|
||||
.filter((msg) => !msg.distributed && msg.senderId !== context.user.id)
|
||||
.map((msg) => msg.id)
|
||||
if (undistributedMessagesIds.length > 0) {
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
const setDistributedCypher = `
|
||||
MATCH (m:Message) WHERE m.id IN $undistributedMessagesIds
|
||||
SET m.distributed = true
|
||||
RETURN m { .* }
|
||||
`
|
||||
const setDistributedTxResponse = await transaction.run(setDistributedCypher, {
|
||||
undistributedMessagesIds,
|
||||
})
|
||||
const messages = await setDistributedTxResponse.records.map((record) => record.get('m'))
|
||||
return messages
|
||||
})
|
||||
try {
|
||||
await writeTxResultPromise
|
||||
} finally {
|
||||
session.close()
|
||||
const session = context.driver.session()
|
||||
try {
|
||||
if (undistributedMessagesIds.length > 0) {
|
||||
await setMessagesAsDistributed(undistributedMessagesIds, session)
|
||||
}
|
||||
// send subscription to author to updated the messages
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
resolved.forEach((message) => {
|
||||
message._id = message.id
|
||||
if (message.senderId !== context.user.id) {
|
||||
message.distributed = true
|
||||
}
|
||||
})
|
||||
// send subscription to author to updated the messages
|
||||
}
|
||||
return resolved.reverse()
|
||||
},
|
||||
@ -61,31 +72,59 @@ export default {
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
const createMessageCypher = `
|
||||
MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId })
|
||||
OPTIONAL MATCH (currentUser)-[:AVATAR_IMAGE]->(image:Image)
|
||||
OPTIONAL MATCH (m:Message)-[:INSIDE]->(room)
|
||||
WITH MAX(m.indexId) as maxIndex, room, currentUser
|
||||
OPTIONAL MATCH (room)<-[:CHATS_IN]-(recipientUser:User)
|
||||
WHERE NOT recipientUser.id = $currentUserId
|
||||
WITH MAX(m.indexId) as maxIndex, room, currentUser, image, recipientUser
|
||||
CREATE (currentUser)-[:CREATED]->(message:Message {
|
||||
createdAt: toString(datetime()),
|
||||
id: apoc.create.uuid(),
|
||||
indexId: CASE WHEN maxIndex IS NOT NULL THEN maxIndex + 1 ELSE 0 END,
|
||||
content: $content,
|
||||
content: LEFT($content,2000),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false
|
||||
})-[:INSIDE]->(room)
|
||||
RETURN message { .* }
|
||||
SET room.lastMessageAt = toString(datetime())
|
||||
RETURN message {
|
||||
.*,
|
||||
indexId: toString(message.indexId),
|
||||
recipientId: recipientUser.id,
|
||||
senderId: currentUser.id,
|
||||
username: currentUser.name,
|
||||
avatar: image.url,
|
||||
date: message.createdAt
|
||||
}
|
||||
`
|
||||
const createMessageTxResponse = await transaction.run(createMessageCypher, {
|
||||
currentUserId,
|
||||
roomId,
|
||||
content,
|
||||
})
|
||||
|
||||
const [message] = await createMessageTxResponse.records.map((record) =>
|
||||
record.get('message'),
|
||||
)
|
||||
|
||||
return message
|
||||
})
|
||||
try {
|
||||
const message = await writeTxResultPromise
|
||||
if (message) {
|
||||
const roomCountUpdated = await getUnreadRoomsCount(message.recipientId, session)
|
||||
|
||||
// send subscriptions
|
||||
void pubsub.publish(ROOM_COUNT_UPDATED, {
|
||||
roomCountUpdated,
|
||||
userId: message.recipientId,
|
||||
})
|
||||
void pubsub.publish(CHAT_MESSAGE_ADDED, {
|
||||
chatMessageAdded: message,
|
||||
userId: message.recipientId,
|
||||
})
|
||||
}
|
||||
|
||||
return message
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
|
||||
@ -7,8 +7,8 @@ export default {
|
||||
notificationAdded: {
|
||||
subscribe: withFilter(
|
||||
() => pubsub.asyncIterator(NOTIFICATION_ADDED),
|
||||
(payload, variables) => {
|
||||
return payload.notificationAdded.to.id === variables.userId
|
||||
(payload, variables, context) => {
|
||||
return payload.notificationAdded.to.id === context.user?.id
|
||||
},
|
||||
),
|
||||
},
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory, { cleanDatabase } from '../../db/factories'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import { createRoomMutation, roomQuery } from '../../graphql/rooms'
|
||||
import { createRoomMutation, roomQuery, unreadRoomsQuery } from '../../graphql/rooms'
|
||||
import { createMessageMutation } from '../../graphql/messages'
|
||||
import createServer from '../../server'
|
||||
|
||||
const driver = getDriver()
|
||||
@ -21,6 +22,9 @@ beforeAll(async () => {
|
||||
driver,
|
||||
neode,
|
||||
user: authenticatedUser,
|
||||
cypherParams: {
|
||||
currentUserId: authenticatedUser ? authenticatedUser.id : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -34,6 +38,8 @@ afterAll(async () => {
|
||||
})
|
||||
|
||||
describe('Room', () => {
|
||||
let roomId: string
|
||||
|
||||
beforeAll(async () => {
|
||||
;[chattingUser, otherChattingUser, notChattingUser] = await Promise.all([
|
||||
Factory.build('user', {
|
||||
@ -48,6 +54,14 @@ describe('Room', () => {
|
||||
id: 'not-chatting-user',
|
||||
name: 'Not Chatting User',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'second-chatting-user',
|
||||
name: 'Second Chatting User',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'third-chatting-user',
|
||||
name: 'Third Chatting User',
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
@ -68,8 +82,6 @@ describe('Room', () => {
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
let roomId: string
|
||||
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await chattingUser.toJson()
|
||||
})
|
||||
@ -122,6 +134,26 @@ describe('Room', () => {
|
||||
CreateRoom: {
|
||||
id: expect.any(String),
|
||||
roomId: result.data.CreateRoom.id,
|
||||
roomName: 'Other Chatting User',
|
||||
unreadCount: 0,
|
||||
users: expect.arrayContaining([
|
||||
{
|
||||
_id: 'chatting-user',
|
||||
id: 'chatting-user',
|
||||
name: 'Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
},
|
||||
{
|
||||
_id: 'other-chatting-user',
|
||||
id: 'other-chatting-user',
|
||||
name: 'Other Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -219,6 +251,7 @@ describe('Room', () => {
|
||||
id: expect.any(String),
|
||||
roomId: result.data.Room[0].id,
|
||||
roomName: 'Chatting User',
|
||||
unreadCount: 0,
|
||||
users: expect.arrayContaining([
|
||||
{
|
||||
_id: 'chatting-user',
|
||||
@ -260,4 +293,319 @@ describe('Room', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('unread rooms query', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
authenticatedUser = null
|
||||
await expect(
|
||||
query({
|
||||
query: unreadRoomsQuery(),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [{ message: 'Not Authorized!' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
let otherRoomId: string
|
||||
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await chattingUser.toJson()
|
||||
const result = await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: 'not-chatting-user',
|
||||
},
|
||||
})
|
||||
otherRoomId = result.data.CreateRoom.roomId
|
||||
await mutate({
|
||||
mutation: createMessageMutation(),
|
||||
variables: {
|
||||
roomId: otherRoomId,
|
||||
content: 'Message to not chatting user',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createMessageMutation(),
|
||||
variables: {
|
||||
roomId,
|
||||
content: '1st message to other chatting user',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createMessageMutation(),
|
||||
variables: {
|
||||
roomId,
|
||||
content: '2nd message to other chatting user',
|
||||
},
|
||||
})
|
||||
authenticatedUser = await otherChattingUser.toJson()
|
||||
const result2 = await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: 'not-chatting-user',
|
||||
},
|
||||
})
|
||||
otherRoomId = result2.data.CreateRoom.roomId
|
||||
await mutate({
|
||||
mutation: createMessageMutation(),
|
||||
variables: {
|
||||
roomId: otherRoomId,
|
||||
content: 'Other message to not chatting user',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('as chatting user', () => {
|
||||
it('has 0 unread rooms', async () => {
|
||||
authenticatedUser = await chattingUser.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: unreadRoomsQuery(),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
UnreadRooms: 0,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('as other chatting user', () => {
|
||||
it('has 1 unread rooms', async () => {
|
||||
authenticatedUser = await otherChattingUser.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: unreadRoomsQuery(),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
UnreadRooms: 1,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('as not chatting user', () => {
|
||||
it('has 2 unread rooms', async () => {
|
||||
authenticatedUser = await notChattingUser.toJson()
|
||||
await expect(
|
||||
query({
|
||||
query: unreadRoomsQuery(),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
UnreadRooms: 2,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('query several rooms', () => {
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await chattingUser.toJson()
|
||||
await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: 'second-chatting-user',
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
mutation: createRoomMutation(),
|
||||
variables: {
|
||||
userId: 'third-chatting-user',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the rooms paginated', async () => {
|
||||
await expect(
|
||||
query({ query: roomQuery(), variables: { first: 3, offset: 0 } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
roomId: expect.any(String),
|
||||
roomName: 'Third Chatting User',
|
||||
lastMessageAt: null,
|
||||
unreadCount: 0,
|
||||
lastMessage: null,
|
||||
users: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
_id: 'chatting-user',
|
||||
id: 'chatting-user',
|
||||
name: 'Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({
|
||||
_id: 'third-chatting-user',
|
||||
id: 'third-chatting-user',
|
||||
name: 'Third Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
roomId: expect.any(String),
|
||||
roomName: 'Second Chatting User',
|
||||
lastMessageAt: null,
|
||||
unreadCount: 0,
|
||||
lastMessage: null,
|
||||
users: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
_id: 'chatting-user',
|
||||
id: 'chatting-user',
|
||||
name: 'Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({
|
||||
_id: 'second-chatting-user',
|
||||
id: 'second-chatting-user',
|
||||
name: 'Second Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
roomId: expect.any(String),
|
||||
roomName: 'Other Chatting User',
|
||||
lastMessageAt: expect.any(String),
|
||||
unreadCount: 0,
|
||||
lastMessage: {
|
||||
_id: expect.any(String),
|
||||
id: expect.any(String),
|
||||
content: '2nd message to other chatting user',
|
||||
senderId: 'chatting-user',
|
||||
username: 'Chatting User',
|
||||
avatar: expect.any(String),
|
||||
date: expect.any(String),
|
||||
saved: true,
|
||||
distributed: false,
|
||||
seen: false,
|
||||
},
|
||||
users: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
_id: 'chatting-user',
|
||||
id: 'chatting-user',
|
||||
name: 'Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({
|
||||
_id: 'other-chatting-user',
|
||||
id: 'other-chatting-user',
|
||||
name: 'Other Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
},
|
||||
})
|
||||
await expect(
|
||||
query({ query: roomQuery(), variables: { first: 3, offset: 3 } }),
|
||||
).resolves.toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
roomId: expect.any(String),
|
||||
roomName: 'Not Chatting User',
|
||||
users: expect.arrayContaining([
|
||||
{
|
||||
_id: 'chatting-user',
|
||||
id: 'chatting-user',
|
||||
name: 'Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
},
|
||||
{
|
||||
_id: 'not-chatting-user',
|
||||
id: 'not-chatting-user',
|
||||
name: 'Not Chatting User',
|
||||
avatar: {
|
||||
url: expect.any(String),
|
||||
},
|
||||
},
|
||||
]),
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('query single room', () => {
|
||||
let result: any = null
|
||||
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await chattingUser.toJson()
|
||||
result = await query({ query: roomQuery() })
|
||||
})
|
||||
|
||||
describe('as chatter of room', () => {
|
||||
it('returns the room', async () => {
|
||||
expect(
|
||||
await query({
|
||||
query: roomQuery(),
|
||||
variables: { first: 2, offset: 0, id: result.data.Room[0].id },
|
||||
}),
|
||||
).toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
roomId: expect.any(String),
|
||||
roomName: result.data.Room[0].roomName,
|
||||
users: expect.any(Array),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('as not chatter of room', () => {
|
||||
beforeAll(async () => {
|
||||
authenticatedUser = await notChattingUser.toJson()
|
||||
})
|
||||
|
||||
it('returns no room', async () => {
|
||||
authenticatedUser = await notChattingUser.toJson()
|
||||
expect(
|
||||
await query({
|
||||
query: roomQuery(),
|
||||
variables: { first: 2, offset: 0, id: result.data.Room[0].id },
|
||||
}),
|
||||
).toMatchObject({
|
||||
errors: undefined,
|
||||
data: {
|
||||
Room: [],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,29 +1,50 @@
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import { pubsub, ROOM_COUNT_UPDATED } from '../../server'
|
||||
import { withFilter } from 'graphql-subscriptions'
|
||||
|
||||
export const getUnreadRoomsCount = async (userId, session) => {
|
||||
return session.readTransaction(async (transaction) => {
|
||||
const unreadRoomsCypher = `
|
||||
MATCH (:User { id: $userId })-[:CHATS_IN]->(room:Room)<-[:INSIDE]-(message:Message)<-[:CREATED]-(sender:User)
|
||||
WHERE NOT sender.id = $userId AND NOT message.seen
|
||||
RETURN toString(COUNT(DISTINCT room)) AS count
|
||||
`
|
||||
const unreadRoomsTxResponse = await transaction.run(unreadRoomsCypher, { userId })
|
||||
return unreadRoomsTxResponse.records.map((record) => record.get('count'))[0]
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
Subscription: {
|
||||
roomCountUpdated: {
|
||||
subscribe: withFilter(
|
||||
() => pubsub.asyncIterator(ROOM_COUNT_UPDATED),
|
||||
(payload, variables, context) => {
|
||||
return payload.userId === context.user?.id
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
Query: {
|
||||
Room: async (object, params, context, resolveInfo) => {
|
||||
if (!params.filter) params.filter = {}
|
||||
params.filter.users_some = {
|
||||
id: context.user.id,
|
||||
}
|
||||
const resolved = await neo4jgraphql(object, params, context, resolveInfo)
|
||||
if (resolved) {
|
||||
resolved.forEach((room) => {
|
||||
if (room.users) {
|
||||
// buggy, you must query the username for this to function correctly
|
||||
room.roomName = room.users.filter((user) => user.id !== context.user.id)[0].name
|
||||
room.avatar =
|
||||
room.users.filter((user) => user.id !== context.user.id)[0].avatar?.url ||
|
||||
'default-avatar'
|
||||
room.users.forEach((user) => {
|
||||
user._id = user.id
|
||||
})
|
||||
}
|
||||
})
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
UnreadRooms: async (object, params, context, resolveInfo) => {
|
||||
const {
|
||||
user: { id: currentUserId },
|
||||
} = context
|
||||
const session = context.driver.session()
|
||||
try {
|
||||
const count = await getUnreadRoomsCount(currentUserId, session)
|
||||
return count
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
return resolved
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
@ -44,7 +65,17 @@ export default {
|
||||
ON CREATE SET
|
||||
room.createdAt = toString(datetime()),
|
||||
room.id = apoc.create.uuid()
|
||||
RETURN room { .* }
|
||||
WITH room, user, currentUser
|
||||
OPTIONAL MATCH (room)<-[:INSIDE]-(message:Message)<-[:CREATED]-(sender:User)
|
||||
WHERE NOT sender.id = $currentUserId AND NOT message.seen
|
||||
WITH room, user, currentUser, message,
|
||||
user.name AS roomName
|
||||
RETURN room {
|
||||
.*,
|
||||
users: [properties(currentUser), properties(user)],
|
||||
roomName: roomName,
|
||||
unreadCount: toString(COUNT(DISTINCT message))
|
||||
}
|
||||
`
|
||||
const createRommTxResponse = await transaction.run(createRoomCypher, {
|
||||
userId,
|
||||
@ -68,6 +99,7 @@ export default {
|
||||
},
|
||||
Room: {
|
||||
...Resolver('Room', {
|
||||
undefinedToNull: ['lastMessageAt'],
|
||||
hasMany: {
|
||||
users: '<-[:CHATS_IN]-(related:User)',
|
||||
},
|
||||
|
||||
@ -3,8 +3,7 @@
|
||||
# }
|
||||
|
||||
enum _MessageOrdering {
|
||||
createdAt_asc
|
||||
createdAt_desc
|
||||
indexId_desc
|
||||
}
|
||||
|
||||
type Message {
|
||||
@ -45,3 +44,7 @@ type Query {
|
||||
orderBy: [_MessageOrdering]
|
||||
): [Message]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
chatMessageAdded: Message
|
||||
}
|
||||
|
||||
@ -38,5 +38,5 @@ type Mutation {
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notificationAdded(userId: ID!): NOTIFIED
|
||||
notificationAdded: NOTIFIED
|
||||
}
|
||||
|
||||
@ -5,6 +5,12 @@
|
||||
# users_some: _UserFilter
|
||||
# }
|
||||
|
||||
# TODO change this to last message date
|
||||
enum _RoomOrdering {
|
||||
lastMessageAt_desc
|
||||
createdAt_desc
|
||||
}
|
||||
|
||||
type Room {
|
||||
id: ID!
|
||||
createdAt: String
|
||||
@ -13,8 +19,28 @@ type Room {
|
||||
users: [User]! @relation(name: "CHATS_IN", direction: "IN")
|
||||
|
||||
roomId: String! @cypher(statement: "RETURN this.id")
|
||||
roomName: String! ## @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user[0].name")
|
||||
avatar: String! ## @cypher match not own user in users array
|
||||
roomName: String! @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.name")
|
||||
avatar: String @cypher(statement: """
|
||||
MATCH (this)<-[:CHATS_IN]-(user:User)
|
||||
WHERE NOT user.id = $cypherParams.currentUserId
|
||||
OPTIONAL MATCH (user)-[:AVATAR_IMAGE]->(image:Image)
|
||||
RETURN image.url
|
||||
""")
|
||||
|
||||
lastMessageAt: String
|
||||
|
||||
lastMessage: Message @cypher(statement: """
|
||||
MATCH (this)<-[:INSIDE]-(message:Message)
|
||||
WITH message ORDER BY message.indexId DESC LIMIT 1
|
||||
RETURN message
|
||||
""")
|
||||
|
||||
unreadCount: Int @cypher(statement: """
|
||||
MATCH (this)<-[:INSIDE]-(message:Message)<-[:CREATED]-(user:User)
|
||||
WHERE NOT user.id = $cypherParams.currentUserId
|
||||
AND NOT message.seen
|
||||
RETURN count(message)
|
||||
""")
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
@ -24,5 +50,13 @@ type Mutation {
|
||||
}
|
||||
|
||||
type Query {
|
||||
Room: [Room]
|
||||
Room(
|
||||
id: ID
|
||||
orderBy: [_RoomOrdering]
|
||||
): [Room]
|
||||
UnreadRooms: Int
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
roomCountUpdated: Int
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ import bodyParser from 'body-parser'
|
||||
import { graphqlUploadExpress } from 'graphql-upload'
|
||||
|
||||
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
|
||||
export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED'
|
||||
export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED'
|
||||
const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG
|
||||
let prodPubsub, devPubsub
|
||||
const options = {
|
||||
|
||||
5
webapp/assets/_new/icons/svgs/align-center.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>align-center</title>
|
||||
<path d="M3 7h26v2h-26v-2zM7 11h18v2h-18v-2zM3 15h26v2h-26v-2zM7 19h18v2h-18v-2zM3 23h26v2h-26v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 274 B |
5
webapp/assets/_new/icons/svgs/align-justify.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>align-justify</title>
|
||||
<path d="M3 7h26v2h-26v-2zM3 11h26v2h-26v-2zM3 15h26v2h-26v-2zM3 19h26v2h-26v-2zM3 23h26v2h-26v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
5
webapp/assets/_new/icons/svgs/align-left.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>align-left</title>
|
||||
<path d="M3 7h26v2h-26v-2zM3 11h18v2h-18v-2zM3 15h26v2h-26v-2zM3 19h18v2h-18v-2zM3 23h26v2h-26v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 272 B |
5
webapp/assets/_new/icons/svgs/align-right.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>align-right</title>
|
||||
<path d="M3 7h26v2h-26v-2zM11 11h18v2h-18v-2zM3 15h26v2h-26v-2zM11 19h18v2h-18v-2zM3 23h26v2h-26v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 275 B |
5
webapp/assets/_new/icons/svgs/angle-left.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>angle-left</title>
|
||||
<path d="M19.031 4.281l1.438 1.438-10.281 10.281 10.281 10.281-1.438 1.438-11-11-0.688-0.719 0.688-0.719z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 279 B |
5
webapp/assets/_new/icons/svgs/angle-right.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>angle-right</title>
|
||||
<path d="M12.969 4.281l11 11 0.688 0.719-0.688 0.719-11 11-1.438-1.438 10.281-10.281-10.281-10.281z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 274 B |
5
webapp/assets/_new/icons/svgs/archive.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>archive</title>
|
||||
<path d="M4 5h24v6h-1v16h-22v-16h-1v-6zM6 7v2h20v-2h-20zM7 11v14h18v-14h-18zM12.813 13c0.013-0.001 0.034-0.001 0.047-0.001s0.034 0.001 0.047 0.001c0.013-0.001 0.034-0.001 0.047-0.001s0.034 0.001 0.047 0.001l6.014-0c0.552 0 1 0.448 1 1s-0.448 1-1 1l-0.014-0h-6c-0.026 0.002-0.068 0.004-0.094 0.004-0.554 0-1.004-0.45-1.004-1.004 0-0.505 0.408-0.953 0.911-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 527 B |
5
webapp/assets/_new/icons/svgs/arrow-up.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>arrow-up</title>
|
||||
<path d="M16 4.094l0.719 0.688 8.5 8.5-1.438 1.438-6.781-6.781v20.063h-2v-20.063l-6.781 6.781-1.438-1.438 8.5-8.5z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 286 B |
5
webapp/assets/_new/icons/svgs/bar-chart.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>bar-chart</title>
|
||||
<path d="M21 4h8v24h-8v-24zM23 6v20h4v-20h-4zM3 10h8v18h-8v-18zM5 12v14h4v-14h-4zM12 16h8v12h-8v-12zM14 18v8h4v-8h-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 290 B |
5
webapp/assets/_new/icons/svgs/briefcase.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>briefcase</title>
|
||||
<path d="M16 3c1.864 0 3.399 1.275 3.844 3h9.156v20h-26v-20h9.156c0.445-1.725 1.98-3 3.844-3zM16 5c-0.81 0-1.428 0.385-1.75 1h3.5c-0.322-0.615-0.94-1-1.75-1zM5 8v9h22v-9h-22zM16 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM5 19v5h22v-5h-22z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 429 B |
5
webapp/assets/_new/icons/svgs/bug.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>bug</title>
|
||||
<path d="M10.719 3.281l2.313 2.313c0.923-0.39 1.922-0.594 2.969-0.594s2.046 0.203 2.969 0.594l2.313-2.313 1.438 1.438-1.938 1.938c1.462 1.119 2.61 2.755 3.344 4.656l2.438-1.219 0.875 1.813-2.75 1.375c0.183 0.876 0.313 1.782 0.313 2.719 0 0.34-0.006 0.666-0.031 1h3.031v2h-3.375c-0.242 1.043-0.561 2.039-1.031 2.938l3 2.25-1.188 1.625-2.938-2.188c-1.618 2.056-3.885 3.375-6.469 3.375s-4.851-1.319-6.469-3.375l-2.938 2.188-1.188-1.625 3-2.25c-0.47-0.898-0.789-1.894-1.031-2.938h-3.375v-2h3.031c-0.025-0.334-0.031-0.66-0.031-1 0-0.937 0.13-1.843 0.313-2.719l-2.75-1.375 0.875-1.813 2.438 1.219c0.734-1.901 1.882-3.538 3.344-4.656l-1.938-1.938zM16 7c-1.978 0-3.827 1.094-5.125 2.875 1.134 0.511 2.924 1.125 5.125 1.125s3.991-0.614 5.125-1.125c-1.298-1.781-3.147-2.875-5.125-2.875zM9.906 11.594c-0.569 1.292-0.906 2.788-0.906 4.406 0 4.629 2.698 8.282 6 8.906v-11.969c-2.17-0.162-3.941-0.801-5.094-1.344zM22.094 11.594c-1.153 0.542-2.924 1.182-5.094 1.344v11.969c3.302-0.625 6-4.278 6-8.906 0-1.618-0.337-3.115-0.906-4.406z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
5
webapp/assets/_new/icons/svgs/calculator.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>calculator</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM9 7h12v6h-12v-6zM11 9v2h8v-2h-8zM10 15h2v2h-2v-2zM14 15h2v2h-2v-2zM18 15h2v2h-2v-2zM10 19h2v2h-2v-2zM14 19h2v2h-2v-2zM18 19h2v2h-2v-2zM10 23h2v2h-2v-2zM14 23h2v2h-2v-2zM18 23h2v2h-2v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 407 B |
5
webapp/assets/_new/icons/svgs/camera.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>camera</title>
|
||||
<path d="M11.5 6h9l0.313 0.406 1.188 1.594h7v18h-26v-18h7l1.188-1.594zM12.5 8l-1.188 1.594-0.313 0.406h-6v14h22v-14h-6l-0.313-0.406-1.188-1.594h-7zM8 11c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM16 11c3.302 0 6 2.698 6 6s-2.698 6-6 6-6-2.698-6-6 2.698-6 6-6zM16 13c-2.221 0-4 1.779-4 4s1.779 4 4 4 4-1.779 4-4-1.779-4-4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 508 B |
5
webapp/assets/_new/icons/svgs/cart-plus.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cart-plus</title>
|
||||
<path d="M4 7h2.219c0.918 0 1.716 0.61 1.938 1.5l2.625 10.5h12.469l2.406-9h2.094l-2.594 9.531c-0.238 0.87-1.004 1.469-1.906 1.469h-12.469c-0.918 0-1.714-0.61-1.938-1.5l-2.625-10.5h-2.219c-0.552 0-1-0.448-1-1s0.448-1 1-1zM22 21c1.645 0 3 1.355 3 3s-1.355 3-3 3-3-1.355-3-3 1.355-3 3-3zM13 21c1.645 0 3 1.355 3 3s-1.355 3-3 3-3-1.355-3-3 1.355-3 3-3zM16 7h2v3h3v2h-3v3h-2v-3h-3v-2h3v-3zM13 23c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1zM22 23c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
5
webapp/assets/_new/icons/svgs/certificate.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>certificate</title>
|
||||
<path d="M16 3c0.624 0 1.248 0.213 1.781 0.594l1.656 1.156 1.875 0.25h0.031c1.314 0.16 2.352 1.223 2.531 2.531 0.003 0.024 0.029 0.038 0.031 0.063h-0.031l0.375 1.875 1.156 1.656c0.762 1.067 0.73 2.476 0.031 3.594v0.031l-0.031 0.031-1.156 1.656-0.25 1.875 3.125 4.75 1.031 1.531h-4.781l-1.156 2.688-0.719 1.719-1.031-1.563-3.156-4.75c-0.818 0.379-1.779 0.349-2.625 0l-3.156 4.75-1.031 1.563-0.719-1.719-1.156-2.688h-4.781l1.031-1.531 3.219-4.906-0.313-1.719-1.188-1.656c-0.762-1.067-0.73-2.507-0.031-3.625v-0.031l0.031-0.031 1.156-1.5 0.25-1.938v-0.031l0.031-0.031c0.283-1.275 1.287-2.279 2.563-2.563l0.031-0.031h0.031l1.906-0.25 1.656-1.156c0.533-0.381 1.157-0.594 1.781-0.594zM16 5.031c-0.229 0-0.458 0.068-0.625 0.188l-2 1.438-0.25 0.031-2.094 0.281c-0.015 0.003-0.016 0.027-0.031 0.031-0.487 0.125-0.875 0.513-1 1-0.004 0.015-0.028 0.016-0.031 0.031l-0.281 2.094-0.031 0.281-0.156 0.188-1.25 1.625c-0.301 0.482-0.269 1.073-0.031 1.406l1.281 1.781 0.156 0.188 0.031 0.25 0.406 2.281v0.063c0.015 0.138 0.063 0.266 0.125 0.375 0.139 0.244 0.378 0.403 0.688 0.438h0.031l2.188 0.313 0.281 0.031 0.188 0.156 1.625 1.25c0.482 0.302 1.073 0.269 1.406 0.031l1.781-1.281 0.188-0.156 0.25-0.031 2.281-0.406h0.063c0.25-0.028 0.45-0.142 0.594-0.313v-0.031l0.063-0.031c0.084-0.122 0.138-0.273 0.156-0.438v-0.031l0.313-2.188 0.031-0.25 1.406-1.969c0.302-0.482 0.269-1.042 0.031-1.375l-1.281-1.781-0.156-0.188-0.031-0.219-0.406-2.219v-0.063c-0.050-0.447-0.365-0.763-0.813-0.813h-0.031l-2.188-0.313-0.25-0.031-0.219-0.156-1.781-1.281c-0.167-0.119-0.396-0.188-0.625-0.188zM22.906 20.25c-0.409 0.323-0.9 0.552-1.438 0.625-0.024 0.003-0.038 0.029-0.063 0.031v-0.031l-1.969 0.344-0.469 0.344 2.125 3.25 0.688-1.594 0.25-0.625h2.406zM9.094 20.281l-1.531 2.313h2.406l0.25 0.625 0.688 1.594 2.125-3.219-0.438-0.344-1.906-0.25c-0.010-0.001-0.021 0.001-0.031 0-0.595-0.072-1.135-0.338-1.563-0.719z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
5
webapp/assets/_new/icons/svgs/chain-broken.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>chain-broken</title>
|
||||
<path d="M21.75 4c1.603 0 3.189 0.626 4.406 1.844 2.435 2.435 2.435 6.409 0 8.844l-1.469 1.469c-1.015 1.016-2.304 1.618-3.625 1.781l-0.25-2c0.901-0.111 1.745-0.494 2.438-1.188h0.031l1.469-1.469c1.671-1.671 1.671-4.36 0-6.031s-4.36-1.671-6.031 0l-1.469 1.469c-0.692 0.692-1.076 1.564-1.188 2.469l-2-0.25c0.162-1.319 0.765-2.609 1.781-3.625l1.469-1.469c1.218-1.218 2.834-1.844 4.438-1.844zM7.719 6.281l4 4-1.438 1.438-4-4zM10.938 14.063l0.25 2c-0.901 0.111-1.745 0.494-2.438 1.188h-0.031l-1.469 1.469c-1.671 1.671-1.671 4.36 0 6.031s4.36 1.671 6.031 0l1.469-1.469c0.692-0.692 1.076-1.564 1.188-2.469l2 0.25c-0.162 1.319-0.765 2.609-1.781 3.625l-1.469 1.469c-2.435 2.435-6.409 2.435-8.844 0s-2.435-6.409 0-8.844l1.469-1.469c1.015-1.016 2.304-1.618 3.625-1.781zM21.719 20.281l4 4-1.438 1.438-4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 968 B |
5
webapp/assets/_new/icons/svgs/chain.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>chain</title>
|
||||
<path d="M21.75 4c1.671 0 3.225 0.661 4.406 1.844s1.844 2.735 1.844 4.406-0.662 3.255-1.844 4.438l-1.469 1.469c-1.181 1.183-2.766 1.844-4.438 1.844-0.793 0-1.565-0.153-2.281-0.438l1.625-1.625c0.215 0.038 0.432 0.063 0.656 0.063 1.138 0 2.226-0.445 3.031-1.25l1.469-1.469c1.66-1.66 1.66-4.372 0-6.031-0.804-0.805-1.863-1.25-3-1.25s-2.227 0.444-3.031 1.25l-1.469 1.469c-0.997 0.996-1.391 2.393-1.188 3.688l-1.625 1.625c-0.285-0.716-0.438-1.487-0.438-2.281 0-1.671 0.662-3.255 1.844-4.438l1.469-1.469c1.181-1.183 2.766-1.844 4.438-1.844zM19.281 11.281l1.438 1.438-8 8-1.438-1.438zM11.75 14c0.793 0 1.565 0.153 2.281 0.438l-1.625 1.625c-0.215-0.038-0.432-0.063-0.656-0.063-1.138 0-2.226 0.445-3.031 1.25l-1.469 1.469c-1.66 1.66-1.66 4.372 0 6.031 0.804 0.805 1.863 1.25 3 1.25s2.227-0.444 3.031-1.25l1.469-1.469c0.997-0.996 1.391-2.393 1.188-3.688l1.625-1.625c0.285 0.716 0.438 1.487 0.438 2.281 0 1.671-0.662 3.256-1.844 4.438l-1.469 1.469c-1.181 1.183-2.766 1.844-4.438 1.844s-3.225-0.661-4.406-1.844c-1.182-1.182-1.844-2.735-1.844-4.406s0.662-3.256 1.844-4.438l1.469-1.469c1.181-1.183 2.766-1.844 4.438-1.844z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
5
webapp/assets/_new/icons/svgs/cloud-download.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cloud-download</title>
|
||||
<path d="M16 5c3.378 0 6.14 2.131 7.344 5.063 3.527 0.182 6.33 2.986 6.563 6.5 1.239 1.102 2.094 2.677 2.094 4.438 0 3.324-2.676 6-6 6h-20c-3.324 0-6-2.676-6-6 0-2.751 1.884-4.944 4.344-5.656 0.597-1.699 2.050-2.919 3.844-3.219 0.454-3.994 3.694-7.125 7.813-7.125zM16 7c-3.37 0-6 2.63-6 6v1h-1c-1.444 0-2.638 0.964-2.938 2.313l-0.125 0.656-0.656 0.125c-1.832 0.319-3.281 1.886-3.281 3.906 0 2.276 1.724 4 4 4h20c2.276 0 4-1.724 4-4 0-1.267-0.65-2.48-1.594-3.188l-0.406-0.313v-0.5c0-2.755-2.245-5-5-5h-1.031l-0.219-0.719c-0.779-2.51-2.988-4.281-5.75-4.281zM15 12h2v6.563l2.281-2.281 1.438 1.438-4 4-0.719 0.688-0.719-0.688-4-4 1.438-1.438 2.281 2.281v-6.563z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 835 B |
5
webapp/assets/_new/icons/svgs/cloud-upload.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cloud-upload</title>
|
||||
<path d="M16 4c3.378 0 6.14 2.131 7.344 5.063 3.527 0.182 6.33 2.986 6.563 6.5 1.239 1.102 2.094 2.677 2.094 4.438 0 3.324-2.676 6-6 6h-20c-3.324 0-6-2.676-6-6 0-2.751 1.884-4.944 4.344-5.656 0.597-1.699 2.050-2.919 3.844-3.219 0.454-3.994 3.694-7.125 7.813-7.125zM16 6c-3.37 0-6 2.63-6 6v1h-1c-1.444 0-2.638 0.964-2.938 2.313l-0.125 0.656-0.656 0.125c-1.832 0.319-3.281 1.886-3.281 3.906 0 2.276 1.724 4 4 4h20c2.276 0 4-1.724 4-4 0-1.267-0.65-2.48-1.594-3.188l-0.406-0.313v-0.5c0-2.755-2.245-5-5-5h-1.031l-0.219-0.719c-0.779-2.51-2.988-4.281-5.75-4.281zM16 11.594l0.719 0.688 4 4-1.438 1.438-2.281-2.281v6.563h-2v-6.563l-2.281 2.281-1.438-1.438 4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 827 B |
5
webapp/assets/_new/icons/svgs/cloud.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cloud</title>
|
||||
<path d="M16 5c2.451 0 4.563 1.302 5.813 3.219 0.392-0.089 0.755-0.219 1.188-0.219 3.302 0 6 2.698 6 6 1.73 1.055 3 2.835 3 5 0 3.302-2.698 6-6 6h-20c-3.302 0-6-2.698-6-6s2.698-6 6-6c0.211 0 0.394 0.040 0.594 0.063 0.531-1.191 1.439-2.083 2.656-2.563 0.698-3.129 3.419-5.5 6.75-5.5zM16 7c-2.522 0-4.581 1.836-4.938 4.25l-0.094 0.688-0.656 0.156c-1.11 0.265-2.002 1.136-2.25 2.25l-0.188 0.969-1-0.219c-0.298-0.067-0.584-0.094-0.875-0.094-2.22 0-4 1.78-4 4s1.78 4 4 4h20c2.22 0 4-1.78 4-4 0-1.662-1.009-3.078-2.438-3.688l-0.656-0.281 0.063-0.719c0.018-0.235 0.031-0.321 0.031-0.313 0-2.22-1.78-4-4-4-0.444 0-0.875 0.096-1.313 0.25l-0.844 0.281-0.375-0.781c-0.824-1.631-2.511-2.75-4.469-2.75z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 858 B |
5
webapp/assets/_new/icons/svgs/code.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>code</title>
|
||||
<path d="M18 5h2l-6 22h-2zM7.938 6.406l1.625 1.188-6.313 8.406 6.313 8.406-1.625 1.188-6.75-9-0.438-0.594 0.438-0.594zM24.063 6.406l6.75 9 0.438 0.594-0.438 0.594-6.75 9-1.625-1.188 6.313-8.406-6.313-8.406z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
5
webapp/assets/_new/icons/svgs/coffee.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>coffee</title>
|
||||
<path d="M16-2h2v4h-2v-4zM20-1h2v3h-2v-3zM10 3h18v4h2c1.645 0 3 1.355 3 3v3c0 1.645-1.355 3-3 3h-2v5c0 1.645-1.355 3-3 3h-12c-1.645 0-3-1.355-3-3v-18zM12 5v16c0 0.555 0.445 1 1 1h12c0.555 0 1-0.445 1-1v-16h-14zM28 9v5h2c0.555 0 1-0.445 1-1v-3c0-0.555-0.445-1-1-1h-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 436 B |
5
webapp/assets/_new/icons/svgs/columns.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>columns</title>
|
||||
<path d="M5 5h22v22h-22v-22zM7 7v18h8v-18h-8zM17 7v18h8v-18h-8z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 234 B |
5
webapp/assets/_new/icons/svgs/compass.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>compass</title>
|
||||
<path d="M16 3c7.168 0 13 5.832 13 13s-5.832 13-13 13-13-5.832-13-13 5.832-13 13-13zM14.875 5.063c-5.226 0.529-9.341 4.695-9.813 9.938h0.938v2h-0.938c0.475 5.284 4.653 9.462 9.938 9.938v-0.938h2v0.938c5.284-0.475 9.462-4.653 9.938-9.938h-0.938v-2h0.938c-0.475-5.284-4.653-9.462-9.938-9.938v0.938h-2v-0.938c-0.041 0.004-0.084-0.004-0.125 0zM22.094 9.906l-3.688 8.5-8.5 3.688 3.688-8.5zM16 14.5c-0.8 0-1.5 0.7-1.5 1.5s0.7 1.5 1.5 1.5 1.5-0.7 1.5-1.5-0.7-1.5-1.5-1.5z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 635 B |
5
webapp/assets/_new/icons/svgs/credit-card.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>credit-card</title>
|
||||
<path d="M5 5h18c1.645 0 3 1.355 3 3v1h1c1.645 0 3 1.355 3 3v12c0 1.645-1.355 3-3 3h-18c-1.645 0-3-1.355-3-3v-1h-1c-1.645 0-3-1.355-3-3v-12c0-1.645 1.355-3 3-3zM5 7c-0.565 0-1 0.435-1 1v12c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-19v-2h19v-1c0-0.565-0.435-1-1-1h-18zM26 11v2h2v-1c0-0.565-0.435-1-1-1h-1zM26 15v5c0 1.645-1.355 3-3 3h-15v1c0 0.565 0.435 1 1 1h18c0.565 0 1-0.435 1-1v-9h-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
5
webapp/assets/_new/icons/svgs/crop.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>crop</title>
|
||||
<path d="M8 4h2v16.563l10.563-10.563h-9.563v-2h11.563l3.719-3.719 1.438 1.438-3.719 3.719v11.563h-2v-9.563l-10.563 10.563h16.563v2h-4v4h-2v-4h-14v-14h-4v-2h4v-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 329 B |
5
webapp/assets/_new/icons/svgs/crosshairs.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>crosshairs</title>
|
||||
<path d="M15 3h2v2.063c5.268 0.477 9.46 4.67 9.938 9.938h2.063v2h-2.063c-0.477 5.268-4.67 9.46-9.938 9.938v2.063h-2v-2.063c-5.268-0.477-9.46-4.67-9.938-9.938h-2.063v-2h2.063c0.477-5.268 4.67-9.46 9.938-9.938v-2.063zM15 7.031c-4.193 0.453-7.515 3.776-7.969 7.969h1.969v2h-1.969c0.453 4.193 3.776 7.515 7.969 7.969v-1.969h2v1.969c4.193-0.453 7.515-3.776 7.969-7.969h-1.969v-2h1.969c-0.453-4.193-3.776-7.515-7.969-7.969v1.969h-2v-1.969z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 607 B |
5
webapp/assets/_new/icons/svgs/cube.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cube</title>
|
||||
<path d="M16 4.406l0.406 0.188 10 4.5 0.594 0.25v12.688l-0.5 0.281-10.5 5.844-0.5-0.281-10.5-5.844v-12.688l0.594-0.25 10-4.5zM16 6.594l-7.688 3.438 7.688 3.844 7.688-3.844zM7 11.625v9.219l8 4.438v-9.656zM25 11.625l-8 4v9.656l8-4.438v-9.219z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
5
webapp/assets/_new/icons/svgs/cubes.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cubes</title>
|
||||
<path d="M16 4l0.375 0.156 6.625 2.656v6.719l5.406 2.344 0.594 0.281v8.063l-0.5 0.313-6 3.344-0.469 0.25-0.469-0.219-5.563-2.781-5.563 2.781-0.469 0.219-0.469-0.25-6-3.344-0.5-0.313v-8.063l0.594-0.281 5.406-2.344v-6.719l6.625-2.656zM16 6.188l-3.281 1.281 3.281 1.281 3.281-1.281zM11 8.938v4.625l4 1.781v-4.875zM21 8.938l-4 1.531v4.875l4-1.781v-4.625zM10 15.313l-3.625 1.563 3.625 1.813 3.625-1.781zM22 15.313l-2.5 1.094-1.125 0.5 3.625 1.781 3.625-1.813zM5 18.406v4.656l4 2.25v-4.906zM27 18.406l-4 2v4.906l4-2.25v-4.656zM15 18.469l-4 1.938v4.969l4-2v-4.906zM17 18.469v4.906l4 2v-4.969z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 754 B |
5
webapp/assets/_new/icons/svgs/cut.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>cut</title>
|
||||
<path d="M6 6c2.197 0 4 1.803 4 4 0 0.494-0.115 0.969-0.281 1.406l6.063 3.438 10.219-5.844h4l-20.281 11.594c0.166 0.438 0.281 0.913 0.281 1.406 0 2.197-1.803 4-4 4s-4-1.803-4-4 1.803-4 4-4c0.981 0 1.864 0.375 2.563 0.969l5.156-2.938-5.219-2.969c-0.691 0.568-1.543 0.938-2.5 0.938-2.197 0-4-1.803-4-4s1.803-4 4-4zM6 8c-0.977 0-1.784 0.677-1.969 1.594-0.026 0.131-0.031 0.267-0.031 0.406 0 1.116 0.884 2 2 2s2-0.884 2-2-0.884-2-2-2zM19.094 16.813l10.906 6.188h-4l-8.906-5.094zM6 20c-0.977 0-1.784 0.677-1.969 1.594-0.026 0.131-0.031 0.267-0.031 0.406 0 1.116 0.884 2 2 2s2-0.884 2-2-0.884-2-2-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 760 B |
5
webapp/assets/_new/icons/svgs/dashboard.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>dashboard</title>
|
||||
<path d="M16 4c6.616 0 12 5.384 12 12s-5.384 12-12 12-12-5.384-12-12 5.384-12 12-12zM16 6c-5.535 0-10 4.465-10 10s4.465 10 10 10 10-4.465 10-10-4.465-10-10-10zM16 7c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM10.344 9.344c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM20.938 9.656l1.438 1.406-4.438 4.438c0.041 0.16 0.063 0.327 0.063 0.5 0 1.105-0.895 2-2 2s-2-0.895-2-2 0.895-2 2-2c0.173 0 0.34 0.021 0.5 0.063zM8 15c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM24 15c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM10.344 20.656c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM21.656 20.656c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 882 B |
5
webapp/assets/_new/icons/svgs/diamond.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>diamond</title>
|
||||
<path d="M9.531 6h12.938l5.313 6.375 0.5 0.594-0.5 0.656-11.781 15-0.781-1-11-14-0.5-0.656 0.5-0.594 5-6zM10.469 8l-3.344 4h4.313l2.688-4h-3.656zM17.875 8l2.688 4h4.313l-3.344-4h-3.656zM16 8.844l-2.125 3.156h4.25zM7.031 14l6.594 8.406-2.375-8.406h-4.219zM13.313 14l2.688 9.313 2.656-9.313h-5.344zM20.75 14l-2.375 8.375 6.594-8.375h-4.219z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
5
webapp/assets/_new/icons/svgs/exchange.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>exchange</title>
|
||||
<path d="M26.188-1.719l6.719 6.719-6.719 6.719-1.406-1.438 4.281-4.281h-21.063v-2h21.063l-4.281-4.281zM13.813 12.281l1.406 1.438-4.281 4.281h21.063v2h-21.063l4.281 4.281-1.406 1.438-6.719-6.719 0.719-0.719z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 378 B |
5
webapp/assets/_new/icons/svgs/exclamation-triangle.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>exclamation-triangle</title>
|
||||
<path d="M16 3.219l0.875 1.5 12 20.781 0.844 1.5h-27.438l0.844-1.5 12-20.781zM16 7.219l-10.25 17.781h20.5zM15 14h2v6h-2v-6zM15 21h2v2h-2v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 324 B |
5
webapp/assets/_new/icons/svgs/expand.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>expand</title>
|
||||
<path d="M14 5h13v13h-2v-9.563l-16.563 16.563h9.563v2h-13v-13h2v9.563l16.563-16.563h-9.563v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 264 B |
5
webapp/assets/_new/icons/svgs/external-link.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>external-link</title>
|
||||
<path d="M18 5h9v9h-2v-5.563l-12.281 12.281-1.438-1.438 12.281-12.281h-5.563v-2zM5 9h13l-2 2h-9v14h14v-9l2-2v13h-18v-18z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 297 B |
5
webapp/assets/_new/icons/svgs/eyedropper.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>eyedropper</title>
|
||||
<path d="M24.656 3.031c1.108 0 2.222 0.41 3.063 1.25 1.681 1.681 1.681 4.444 0 6.125l-2.813 2.781 1 1-1.406 1.406-1-1-9.5 9.5c-1.064 1.064-1.845 1.684-2.531 2.063s-1.277 0.493-1.688 0.563-0.636 0.113-1.063 0.344-1.040 0.696-2 1.656l-0.719 0.688-0.719-0.688-2-2-0.688-0.719 0.688-0.719c0.986-0.986 1.475-1.621 1.719-2.063s0.276-0.66 0.344-1.063 0.196-1.011 0.563-1.688 0.96-1.429 2-2.469l9.5-9.5-1-1 1.406-1.406 1 1 2.781-2.813c0.84-0.84 1.954-1.25 3.063-1.25zM24.656 5.031c-0.592 0-1.197 0.228-1.656 0.688l-2.781 2.781 3.281 3.281 2.781-2.781c0.919-0.919 0.919-2.362 0-3.281-0.46-0.46-1.033-0.688-1.625-0.688zM18.813 9.906l-9.5 9.5c-0.96 0.96-1.426 1.605-1.656 2.031s-0.274 0.621-0.344 1.031-0.184 1.033-0.563 1.719c-0.259 0.469-0.859 1.1-1.406 1.719l0.75 0.75c0.601-0.529 1.227-1.126 1.688-1.375 0.677-0.366 1.254-0.463 1.656-0.531s0.621-0.1 1.063-0.344 1.108-0.733 2.094-1.719l9.5-9.5z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
5
webapp/assets/_new/icons/svgs/facebook.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>facebook</title>
|
||||
<path d="M7 5h18c1.093 0 2 0.907 2 2v18c0 1.093-0.907 2-2 2h-18c-1.093 0-2-0.907-2-2v-18c0-1.093 0.907-2 2-2zM7 7v18h9.688v-6.75h-2.625v-3h2.625v-2.25c0-2.583 1.571-3.969 3.875-3.969 1.104 0 2.067 0.057 2.344 0.094v2.719h-1.625c-1.253 0-1.469 0.595-1.469 1.469v1.938h2.969l-0.375 3h-2.594v6.75h5.188v-18h-18z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 480 B |
5
webapp/assets/_new/icons/svgs/female.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>female</title>
|
||||
<path d="M18.125 4h0.594l0.281 0.5 0.938 1.656c1.545 0.156 3.628 0.829 5.438 3.25 2.055 2.749 3.625 7.468 3.625 15.594v1h-9.656c-0.989 0.617-2.104 1-3.344 1s-2.355-0.375-3.344-1h-9.656v-1c0-9.134 1.977-14.423 4.969-17.438s6.852-3.563 10.156-3.563zM17.563 6.063c-2.914 0.059-5.867 0.568-8.188 2.906-2.366 2.384-4.16 6.895-4.313 15.031h5.406c-0.119-0.152-0.233-0.311-0.344-0.469-1.357-1.941-2.125-4.333-2.125-6.531 0-0.783 0.212-1.515 0.625-2.063s0.978-0.894 1.563-1.125c1.169-0.461 2.477-0.521 3.719-0.625s2.43-0.242 3.125-0.563 0.969-0.581 0.969-1.625h2c0 1.66-0.976 2.893-2.156 3.438s-2.492 0.644-3.75 0.75-2.45 0.221-3.156 0.5c-0.353 0.139-0.585 0.292-0.719 0.469s-0.219 0.4-0.219 0.844c0 1.711 0.643 3.824 1.75 5.406s2.587 2.594 4.25 2.594c1.665 0 3.144-1.014 4.25-2.594s1.75-3.684 1.75-5.406h2c0 2.207-0.767 4.624-2.125 6.563-0.102 0.146-0.203 0.297-0.313 0.438h5.375c-0.136-7.17-1.553-11.261-3.156-13.406-1.684-2.253-3.521-2.594-4.531-2.594h-0.594l-0.281-0.5zM13 17c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM19 17c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
5
webapp/assets/_new/icons/svgs/file-archive.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-archive-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-7v1h-2v-1h-7zM14 7h2v2h-2v-2zM14 10h2v2h-2v-2zM14 13h2v2.188c1.156 0.418 2 1.52 2 2.813 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-1.292 0.844-2.394 2-2.813v-2.188zM15 17c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 442 B |
5
webapp/assets/_new/icons/svgs/file-audio.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-audio-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM15 9.719l5.25 1.313-0.5 1.938-2.75-0.688v6.719c0 1.645-1.355 3-3 3s-3-1.355-3-3 1.355-3 3-3c0.353 0 0.684 0.073 1 0.188v-6.469zM14 18c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 415 B |
5
webapp/assets/_new/icons/svgs/file-code.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-code-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM15 13h2l-2 12h-2zM11.219 15.375l1.563 1.25-1.969 2.375 1.969 2.375-1.563 1.25-2.5-3-0.531-0.625 0.531-0.625zM18.781 15.375l2.5 3 0.531 0.625-0.531 0.625-2.5 3-1.563-1.25 1.969-2.375-1.969-2.375z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 481 B |
5
webapp/assets/_new/icons/svgs/file-excel.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-excel-o</title>
|
||||
<path d="M5 3h20v7h-2v-5h-16v5h-2v-7zM7 12h2l1 2 1-2h2l-2 4 2 4h-2l-1-2-1 2h-2l2-4zM14 12h2v6h2v2h-4v-8zM21.5 12c0.733 0 1.402 0.287 1.844 0.75s0.656 1.068 0.656 1.656h-2c0-0.112-0.035-0.22-0.094-0.281s-0.14-0.125-0.406-0.125c-0.217 0-0.5 0.283-0.5 0.5s0.283 0.5 0.5 0.5c1.383 0 2.5 1.117 2.5 2.5 0 1.3-1.081 2.5-2.5 2.5-0.732 0-1.413-0.232-1.875-0.719s-0.625-1.121-0.625-1.688h2c0 0.233 0.049 0.299 0.063 0.313s0.069 0.094 0.438 0.094c0.381 0 0.5-0.2 0.5-0.5 0-0.217-0.283-0.5-0.5-0.5-1.383 0-2.5-1.117-2.5-2.5s1.117-2.5 2.5-2.5zM5 22h2v5h16v-5h2v7h-20v-7z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 733 B |
5
webapp/assets/_new/icons/svgs/file-image.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-image-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM20.094 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM13 15.594l0.719 0.688 2.281 2.281 1.281-1.281 0.719-0.688 0.719 0.688 3 3-1.438 1.438-2.281-2.281-1.281 1.281-0.719 0.688-0.719-0.688-2.281-2.281-3.281 3.281-1.438-1.438 4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 530 B |
5
webapp/assets/_new/icons/svgs/file-movie.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-movie-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM12 13.219l1.5 0.938 5 3 1.438 0.844-1.438 0.844-5 3-1.5 0.938v-9.563zM14 16.75v2.5l2.094-1.25z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 382 B |
5
webapp/assets/_new/icons/svgs/file-pdf.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-pdf-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM14.406 10.344h0.031c0.33 0.007 0.656 0.135 0.906 0.344 0.257 0.215 0.428 0.498 0.531 0.781 0.207 0.567 0.235 1.188 0.188 1.906-0.039 0.595-0.326 1.352-0.469 2.031 0.187 0.42 0.248 0.774 0.469 1.188 0.409 0.766 0.86 1.217 1.313 1.813 0.517-0.027 1.127-0.182 1.563-0.125 0.567 0.074 1.089 0.186 1.5 0.625 0.206 0.22 0.369 0.553 0.375 0.875s-0.098 0.607-0.25 0.875v0.031h-0.031c-0.345 0.586-0.969 0.976-1.594 0.938s-1.148-0.368-1.625-0.781c-0.236-0.205-0.429-0.616-0.656-0.875-0.529 0.060-0.906-0.001-1.469 0.125-0.537 0.12-0.902 0.332-1.406 0.5-0.334 0.672-0.599 1.509-0.969 2-0.4 0.531-0.818 0.984-1.406 1.188-0.294 0.102-0.645 0.097-0.969-0.031s-0.566-0.349-0.75-0.625c-0.372-0.529-0.404-1.263-0.125-1.781s0.747-0.887 1.281-1.219c0.496-0.308 1.245-0.45 1.875-0.688 0.276-0.598 0.576-0.984 0.813-1.656 0.275-0.783 0.321-1.455 0.5-2.219-0.35-0.837-0.787-1.712-0.938-2.438-0.128-0.62-0.169-1.181-0.031-1.719 0.069-0.269 0.184-0.535 0.438-0.75 0.246-0.208 0.601-0.317 0.906-0.313zM15.063 17.75c-0.060 0.184-0.061 0.348-0.125 0.531-0.055 0.157-0.13 0.252-0.188 0.406 0.048-0.011 0.077-0.052 0.125-0.063 0.285-0.064 0.499-0.012 0.781-0.063-0.193-0.295-0.413-0.496-0.594-0.813zM18.75 19.781c-0.064-0.008-0.211 0.037-0.281 0.031 0.243 0.17 0.495 0.308 0.563 0.313 0.080 0.005 0.096 0.018 0.219-0.188 0.012-0.021-0.007-0.015 0-0.031-0.045-0.019-0.163-0.081-0.5-0.125zM11.75 21.344c-0.031 0.019-0.095 0.044-0.125 0.063-0.409 0.254-0.646 0.522-0.719 0.656s-0.103 0.072 0 0.219h0.031c0.020 0.030-0.012 0.018 0 0.031 0.071-0.030 0.351-0.219 0.656-0.625 0.054-0.072 0.101-0.262 0.156-0.344z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
5
webapp/assets/_new/icons/svgs/file-photo.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-photo-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM20.094 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM13 15.594l0.719 0.688 2.281 2.281 1.281-1.281 0.719-0.688 0.719 0.688 3 3-1.438 1.438-2.281-2.281-1.281 1.281-0.719 0.688-0.719-0.688-2.281-2.281-3.281 3.281-1.438-1.438 4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 530 B |
5
webapp/assets/_new/icons/svgs/file-picture.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-picture-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM20.094 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM13 15.594l0.719 0.688 2.281 2.281 1.281-1.281 0.719-0.688 0.719 0.688 3 3-1.438 1.438-2.281-2.281-1.281 1.281-0.719 0.688-0.719-0.688-2.281-2.281-3.281 3.281-1.438-1.438 4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 532 B |
5
webapp/assets/_new/icons/svgs/file-powerpoint.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-powerpoint-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM12 11h4c2.21 0 4 1.79 4 4s-1.79 4-4 4c-0.74 0-1.406-0.244-2-0.594v3.594h-2v-7h2c0 1.19 0.81 2 2 2s2-0.81 2-2-0.81-2-2-2h-4v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 355 B |
5
webapp/assets/_new/icons/svgs/file-sound.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-sound-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM15 9.719l5.25 1.313-0.5 1.938-2.75-0.688v6.719c0 1.645-1.355 3-3 3s-3-1.355-3-3 1.355-3 3-3c0.353 0 0.684 0.073 1 0.188v-6.469zM14 18c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 415 B |
5
webapp/assets/_new/icons/svgs/file-text.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-text</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM10 13h10v2h-10v-2zM10 17h10v2h-10v-2zM10 21h10v2h-10v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 341 B |
5
webapp/assets/_new/icons/svgs/file-video.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-video-o</title>
|
||||
<path d="M5 3h13.406l0.313 0.281 6 6 0.281 0.313v19.406h-20v-26zM7 5v22h16v-16h-6v-6h-10zM19 6.438v2.563h2.563zM12 13.219l1.5 0.938 5 3 1.438 0.844-1.438 0.844-5 3-1.5 0.938v-9.563zM14 16.75v2.5l2.094-1.25z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 382 B |
5
webapp/assets/_new/icons/svgs/file-word.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-word-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-16zM17 12h4v2h-2v4.5c0 1.383-1.117 2.5-2.5 2.5-0.386 0-0.604-0.283-0.906-0.469-0.408 0.824-1.11 1.469-2.094 1.469-1.383 0-2.5-1.117-2.5-2.5v-4.5h-2v-2h4v6.5c0 0.217 0.283 0.5 0.5 0.5s0.5-0.283 0.5-0.5v-4.5h2v2s0.007 0.652 0.156 1.25c0.075 0.299 0.198 0.577 0.281 0.688s0.021 0.063 0.063 0.063c0.217 0 0.5-0.283 0.5-0.5v-6.5z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 543 B |
5
webapp/assets/_new/icons/svgs/file-zip.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>file-zip-o</title>
|
||||
<path d="M5 3h20v26h-20v-26zM7 5v22h16v-22h-7v1h-2v-1h-7zM14 7h2v2h-2v-2zM14 10h2v2h-2v-2zM14 13h2v2.188c1.156 0.418 2 1.52 2 2.813 0 1.645-1.355 3-3 3s-3-1.355-3-3c0-1.292 0.844-2.394 2-2.813v-2.188zM15 17c-0.564 0-1 0.436-1 1s0.436 1 1 1 1-0.436 1-1-0.436-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 438 B |
5
webapp/assets/_new/icons/svgs/files.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>files-o</title>
|
||||
<path d="M4 4h16v3h-2v-1h-12v16h5v2h-7v-20zM12 8h16v20h-16v-20zM14 10v16h12v-16h-12z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
5
webapp/assets/_new/icons/svgs/film.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>film</title>
|
||||
<path d="M4 4h24v24h-24v-24zM6 6v20h2v-1h2v1h12v-1h2v1h2v-20h-2v1h-2v-1h-12v1h-2v-1h-2zM8 9h2v2h-2v-2zM22 9h2v2h-2v-2zM8 13h2v2h-2v-2zM22 13h2v2h-2v-2zM8 17h2v2h-2v-2zM22 17h2v2h-2v-2zM8 21h2v2h-2v-2zM22 21h2v2h-2v-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 385 B |
5
webapp/assets/_new/icons/svgs/fire.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>fire</title>
|
||||
<path d="M13.375 3h1.281l0.281 0.219s2.507 2.024 5 5.094 5.063 7.224 5.063 11.688c0 1.978-0.333 4.263-1.938 6.063-1.369 1.535-3.592 2.567-6.938 2.844-0.085 0.007-0.163 0.025-0.25 0.031-0.283 0.028-0.57 0.063-0.875 0.063-0.097 0-0.186-0.028-0.281-0.031-0.139 0.002-0.263 0.031-0.406 0.031-3.265 0-5.674-1.113-7.188-2.781s-2.125-3.807-2.125-5.813c0-4.244 1.984-7.63 3.969-10.344s3.919-4.935 4.219-6.281zM14.5 5.5c-0.868 1.866-2.366 3.645-3.906 5.75-1.892 2.586-3.594 5.534-3.594 9.156 0 1.595 0.508 3.237 1.625 4.469 0.238 0.262 0.514 0.493 0.813 0.719-0.078-0.193-0.164-0.391-0.219-0.594-0.619-2.311 0.099-5.073 1.969-7.594l0.938-1.281 0.75 1.406c0.511 0.955 1.047 1.345 1.344 1.438s0.424 0.063 0.719-0.281c0.589-0.689 1.141-3.002 0.094-6.406l-0.375-1.281h1.938l0.281 0.344c0.548 0.633 1.188 1.78 1.938 3.406s1.529 3.644 1.938 5.656c0.358 1.761 0.476 3.535-0.063 5.094 0.34-0.241 0.632-0.509 0.875-0.781 1.13-1.267 1.438-2.963 1.438-4.719 0-3.669-2.272-7.509-4.625-10.406-1.705-2.099-3.067-3.383-3.875-4.094zM17.563 17.219c-0.157 1.133-0.503 2.089-1.094 2.781-0.688 0.806-1.824 1.195-2.844 0.875-0.551-0.173-1.025-0.508-1.469-0.969-0.903 1.704-1.324 3.385-1 4.594 0.392 1.464 1.431 2.428 3.594 2.5 0.086 0.003 0.16 0 0.25 0 0.345-0.011 0.686-0.037 1-0.063 0.15-0.018 0.303-0.036 0.438-0.063 1.21-0.239 1.804-0.811 2.188-1.594 0.511-1.044 0.519-2.681 0.156-4.469-0.25-1.23-0.756-2.418-1.219-3.594z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
5
webapp/assets/_new/icons/svgs/flask.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>flask</title>
|
||||
<path d="M17 0c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM14 3c0.552 0 1 0.448 1 1h7v2h-1v7.656l6.156 7.938c0.543 0.698 0.844 1.553 0.844 2.438 0 2.185-1.784 3.969-3.969 3.969h-16.063c-2.185 0-3.969-1.784-3.969-3.969 0-0.885 0.301-1.74 0.844-2.438l6.156-7.938v-7.656h-1v-2h3c0-0.552 0.448-1 1-1zM13 6v8.344l-2.844 3.656h11.688l-2.844-3.656v-8.344h-6zM17 10c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM8.594 20l-2.188 2.813c-0.269 0.346-0.406 0.779-0.406 1.219 0 1.105 0.864 1.969 1.969 1.969h16.063c1.105 0 1.969-0.864 1.969-1.969 0-0.439-0.137-0.873-0.406-1.219l-2.188-2.813h-14.813z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 782 B |
5
webapp/assets/_new/icons/svgs/floppy.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>floppy-o</title>
|
||||
<path d="M5 5h17.406l0.313 0.281 4 4 0.281 0.313v17.406h-22v-22zM7 7v18h2v-9h14v9h2v-14.563l-3-3v5.563h-12v-6h-3zM12 7v4h8v-4h-2v2h-2v-2h-4zM11 18v7h10v-7h-10z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 331 B |
5
webapp/assets/_new/icons/svgs/folder-open.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>folder-open</title>
|
||||
<path d="M5 3h22v10.406l-0.281 0.313-1.719 1.719v12.563h-6v2.719l-1.219-0.25-12.781-2.656v-24.813zM14.125 5l2.875 0.719v9.344l1.719 1.719 0.281 0.313v8.906h4v-11.406l0.281-0.313 1.719-1.719v-7.563h-10.875zM7 5.281v20.906l10 2.094v-10.344l-1.719-1.719-0.281-0.313v-8.625z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 445 B |
5
webapp/assets/_new/icons/svgs/folder.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>folder</title>
|
||||
<path d="M6 3h22v10.406l-0.281 0.313-1.719 1.719v13.563h-20v-26zM8 5v22h16v-11.563l-1.719-1.719-0.281-0.313v-8.406h-14zM24 5v7.563l1 1 1-1v-7.563h-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 319 B |
5
webapp/assets/_new/icons/svgs/frown.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>frown-o</title>
|
||||
<path d="M16 4c6.616 0 12 5.384 12 12s-5.384 12-12 12-12-5.384-12-12 5.384-12 12-12zM16 6c-5.535 0-10 4.465-10 10s4.465 10 10 10 10-4.465 10-10-4.465-10-10-10zM11.5 12c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5zM20.5 12c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5-1.5-0.672-1.5-1.5 0.672-1.5 1.5-1.5zM16 18c2.667 0 5.020 1.335 6.469 3.344l-1.625 1.156c-1.092-1.514-2.835-2.5-4.844-2.5s-3.752 0.986-4.844 2.5l-1.625-1.156c1.448-2.008 3.801-3.344 6.469-3.344z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 667 B |
5
webapp/assets/_new/icons/svgs/gamepad.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>gamepad</title>
|
||||
<path d="M16 7c6.383 0 11.969 2.063 11.969 2.063l0.594 0.219 0.063 0.594 1.344 10.25c0.365 2.835-1.665 5.479-4.5 5.844-2.639 0.34-5.005-1.44-5.625-3.969h-7.688c-0.619 2.529-2.985 4.309-5.625 3.969-2.835-0.365-4.865-3.009-4.5-5.844l1.344-10.25 0.063-0.594 0.594-0.219s5.586-2.063 11.969-2.063zM16 9c-5.486 0-10.007 1.524-10.75 1.781l-1.219 9.625c-0.227 1.763 0.987 3.335 2.75 3.563s3.368-0.987 3.594-2.75l0.031-0.344 0.125-0.875h10.938l0.125 0.875 0.031 0.344c0.227 1.763 1.832 2.977 3.594 2.75 1.763-0.227 2.977-1.8 2.75-3.563l-1.219-9.625c-0.743-0.258-5.264-1.781-10.75-1.781zM9 12h2v2h2v2h-2v2h-2v-2h-2v-2h2v-2zM22 12c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM20 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM24 14c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1zM22 16c0.552 0 1 0.448 1 1s-0.448 1-1 1-1-0.448-1-1 0.448-1 1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
5
webapp/assets/_new/icons/svgs/gear.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>gear</title>
|
||||
<path d="M13.188 3h5.625l0.156 0.813 0.594 2.969c0.951 0.374 1.814 0.903 2.594 1.531l2.906-1 0.781-0.25 0.406 0.719 2 3.438 0.406 0.719-0.594 0.531-2.25 1.969c0.084 0.513 0.188 1.022 0.188 1.563s-0.104 1.050-0.188 1.563l2.25 1.969 0.594 0.531-0.406 0.719-2 3.438-0.406 0.719-0.781-0.25-2.906-1c-0.78 0.628-1.642 1.157-2.594 1.531l-0.594 2.969-0.156 0.813h-5.625l-0.156-0.813-0.594-2.969c-0.951-0.374-1.814-0.903-2.594-1.531l-2.906 1-0.781 0.25-0.406-0.719-2-3.438-0.406-0.719 0.594-0.531 2.25-1.969c-0.084-0.513-0.188-1.022-0.188-1.563s0.104-1.050 0.188-1.563l-2.25-1.969-0.594-0.531 0.406-0.719 2-3.438 0.406-0.719 0.781 0.25 2.906 1c0.78-0.628 1.642-1.157 2.594-1.531l0.594-2.969zM14.813 5l-0.5 2.594-0.125 0.594-0.563 0.188c-1.138 0.355-2.169 0.955-3.031 1.75l-0.438 0.406-0.563-0.188-2.531-0.875-1.188 2.031 2 1.781 0.469 0.375-0.156 0.594c-0.128 0.57-0.188 1.153-0.188 1.75s0.060 1.18 0.188 1.75l0.156 0.594-0.469 0.375-2 1.781 1.188 2.031 2.531-0.875 0.563-0.188 0.438 0.406c0.863 0.795 1.894 1.395 3.031 1.75l0.563 0.188 0.125 0.594 0.5 2.594h2.375l0.5-2.594 0.125-0.594 0.563-0.188c1.138-0.355 2.169-0.955 3.031-1.75l0.438-0.406 0.563 0.188 2.531 0.875 1.188-2.031-2-1.781-0.438-0.375 0.125-0.594c0.128-0.572 0.188-1.153 0.188-1.75s-0.060-1.18-0.188-1.75l-0.156-0.594 0.469-0.375 2-1.781-1.188-2.031-2.531 0.875-0.563 0.188-0.438-0.406c-0.863-0.795-1.894-1.395-3.031-1.75l-0.563-0.188-0.125-0.594-0.5-2.594h-2.375zM16 11c2.75 0 5 2.25 5 5s-2.25 5-5 5-5-2.25-5-5 2.25-5 5-5zM16 13c-1.669 0-3 1.331-3 3s1.331 3 3 3 3-1.331 3-3-1.331-3-3-3z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
5
webapp/assets/_new/icons/svgs/gears.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>gears</title>
|
||||
<path d="M21.5 2.5h2v1.406c0.828 0.146 1.586 0.474 2.25 0.938l0.938-0.938 1.406 1.406-0.938 0.938c0.464 0.664 0.792 1.421 0.938 2.25h1.406v2h-1.406c-0.147 0.837-0.466 1.613-0.938 2.281l0.969 1.031-1.469 1.375-0.938-1c-0.656 0.451-1.405 0.763-2.219 0.906v1.406h-2v-1.406c-0.828-0.146-1.586-0.474-2.25-0.938l-1.031 1.063-1.438-1.438 1.063-1.031c-0.464-0.664-0.792-1.421-0.938-2.25h-1.406v-2h1.406c0.143-0.814 0.456-1.563 0.906-2.219l-1-0.938 1.375-1.469 1.031 0.969c0.669-0.471 1.445-0.79 2.281-0.938v-1.406zM22.5 5.813c-2.055 0-3.688 1.632-3.688 3.688s1.632 3.688 3.688 3.688 3.688-1.632 3.688-3.688-1.632-3.688-3.688-3.688zM9.531 11.719l0.719 1.813c0.53-0.132 1.087-0.219 1.656-0.219 0.571 0 1.126 0.085 1.656 0.219l0.719-1.813 1.844 0.75-0.719 1.813c0.944 0.571 1.742 1.372 2.313 2.313l1.813-0.719 0.75 1.844-1.813 0.719c0.132 0.529 0.219 1.087 0.219 1.656s-0.086 1.126-0.219 1.656l1.813 0.719-0.75 1.844-1.813-0.719c-0.569 0.952-1.367 1.766-2.313 2.344l0.719 1.781-1.844 0.75-0.719-1.781c-0.529 0.134-1.087 0.219-1.656 0.219-0.573 0-1.123-0.084-1.656-0.219l-0.719 1.781-1.844-0.75 0.719-1.781c-0.961-0.577-1.765-1.384-2.344-2.344l-1.781 0.719-0.75-1.844 1.781-0.719c-0.134-0.53-0.219-1.087-0.219-1.656s0.085-1.128 0.219-1.656l-1.781-0.719 0.75-1.844 1.781 0.719c0.577-0.945 1.392-1.743 2.344-2.313l-0.719-1.813zM11.906 15.313c-2.663 0-4.813 2.118-4.813 4.781s2.15 4.813 4.813 4.813 4.781-2.15 4.781-4.813-2.118-4.781-4.781-4.781z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
5
webapp/assets/_new/icons/svgs/gift.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>gift</title>
|
||||
<path d="M12 5c1.749 0 2.939 1.329 3.719 2.438 0.104 0.148 0.189 0.293 0.281 0.438 0.092-0.145 0.177-0.289 0.281-0.438 0.779-1.108 1.97-2.438 3.719-2.438 1.645 0 3 1.355 3 3 0 0.353-0.073 0.684-0.188 1h5.188v6h-1v13h-22v-13h-1v-6h5.188c-0.114-0.316-0.188-0.647-0.188-1 0-1.645 1.355-3 3-3zM12 7c-0.565 0-1 0.435-1 1s0.435 1 1 1h2.313c-0.121-0.206-0.097-0.22-0.25-0.438-0.627-0.892-1.436-1.563-2.063-1.563zM20 7c-0.626 0-1.436 0.671-2.063 1.563-0.153 0.217-0.129 0.232-0.25 0.438h2.313c0.565 0 1-0.435 1-1s-0.435-1-1-1zM6 11v2h9v-1h2v1h9v-2h-20zM7 15v11h8v-10h2v10h8v-11h-18z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
5
webapp/assets/_new/icons/svgs/github.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>github</title>
|
||||
<path d="M15.999 4c6.628 0 12.001 5.373 12.001 12.001 0 5.3-3.435 9.794-8.2 11.385-0.609 0.117-0.825-0.256-0.825-0.577 0-0.394 0.015-1.688 0.015-3.292 0-1.119-0.384-1.852-0.815-2.222 2.673-0.297 5.479-1.311 5.479-5.921 0-1.31-0.464-2.381-1.233-3.22 0.124-0.304 0.536-1.524-0.119-3.176 0 0-1.006-0.323-3.297 1.23-0.958-0.266-1.985-0.399-3.004-0.404-1.020 0.005-2.047 0.138-3.004 0.404-2.292-1.553-3.3-1.23-3.3-1.23-0.653 1.652-0.241 2.872-0.118 3.176-0.767 0.839-1.235 1.91-1.235 3.22 0 4.599 2.801 5.628 5.466 5.931-0.343 0.3-0.653 0.829-0.762 1.604-0.683 0.307-2.422 0.837-3.492-0.997 0 0-0.634-1.152-1.838-1.237 0 0-1.172-0.016-0.082 0.729 0 0 0.786 0.369 1.332 1.755 0 0 0.704 2.334 4.042 1.609 0.006 1.001 0.016 1.756 0.016 2.041 0 0.318-0.219 0.688-0.819 0.578-4.769-1.587-8.207-6.085-8.207-11.386 0-6.628 5.373-12.001 11.999-12.001z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1008 B |
5
webapp/assets/_new/icons/svgs/glass.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>glass</title>
|
||||
<path d="M8.25 5h15.5l0.25 0.594c0.585 1.326 1 3.076 1 5.406 0 4.616-3.516 8.431-8 8.938v6.063h5v2h-12v-2h5v-6.063c-4.484-0.506-8-4.322-8-8.938 0-2.325 0.413-4.077 1-5.406zM9.656 7c-0.362 1.001-0.656 2.235-0.656 4 0 3.877 3.123 7 7 7 3.535 0 6.421-2.603 6.906-6h-11.906v-2h11.938c-0.081-1.241-0.277-2.207-0.563-3h-12.719z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 490 B |
5
webapp/assets/_new/icons/svgs/group.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>group</title>
|
||||
<path d="M8 7c3.302 0 6 2.698 6 6 0 1.984-0.975 3.75-2.469 4.844 1.459 0.725 2.68 1.862 3.469 3.281 0.789-1.42 2.010-2.557 3.469-3.281-1.494-1.094-2.469-2.86-2.469-4.844 0-3.302 2.698-6 6-6s6 2.698 6 6c0 1.984-0.975 3.75-2.469 4.844 2.638 1.31 4.469 4.020 4.469 7.156h-2c0-3.326-2.674-6-6-6s-6 2.674-6 6h-2c0-3.326-2.674-6-6-6s-6 2.674-6 6h-2c0-3.136 1.831-5.846 4.469-7.156-1.494-1.094-2.469-2.86-2.469-4.844 0-3.302 2.698-6 6-6zM8 9c-2.221 0-4 1.779-4 4s1.779 4 4 4 4-1.779 4-4-1.779-4-4-4zM22 9c-2.221 0-4 1.779-4 4s1.779 4 4 4 4-1.779 4-4-1.779-4-4-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 724 B |
5
webapp/assets/_new/icons/svgs/hand-down.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-o-down</title>
|
||||
<path d="M10 2h16v16.844c0 1.409-0.998 2.642-2.375 2.938l-7.625 1.656v3.563c0 1.645-1.355 3-3 3s-3-1.355-3-3v-10.75l-0.75 0.188c-0.156 0.203-0.224 0.331-0.625 0.625-0.642 0.47-1.633 0.938-2.969 0.938-1.426 0.002-2.656-1.29-2.656-2.906v-0.406l0.281-0.313 6.719-6.781v-5.594zM12 4v3h12v-3h-12zM11.406 9l-6.313 6.406c0.082 0.421 0.255 0.594 0.563 0.594 0.903 0 1.459-0.273 1.813-0.531s0.438-0.438 0.438-0.438l0.188-0.344 0.406-0.125 2.25-0.594 1.25-0.313v13.344c0 0.565 0.435 1 1 1s1-0.435 1-1v-5.188l0.781-0.188 8.438-1.781c0.467-0.1 0.781-0.523 0.781-1v-9.844h-12.594z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
5
webapp/assets/_new/icons/svgs/hand-left.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-o-left</title>
|
||||
<path d="M16.906 3h0.406l0.313 0.281 6.781 6.719h5.594v16h-16.844c-1.409 0-2.642-0.998-2.938-2.375l-1.656-7.625h-3.563c-1.645 0-3-1.355-3-3s1.355-3 3-3h10.75l-0.188-0.75c-0.203-0.156-0.331-0.224-0.625-0.625-0.47-0.642-0.938-1.633-0.938-2.969-0.002-1.426 1.29-2.656 2.906-2.656zM16.594 5.094c-0.421 0.082-0.594 0.255-0.594 0.563 0 0.903 0.273 1.459 0.531 1.813s0.438 0.438 0.438 0.438l0.344 0.188 0.125 0.406 0.594 2.25 0.313 1.25h-13.344c-0.565 0-1 0.435-1 1s0.435 1 1 1h5.188l0.188 0.781 1.781 8.438c0.1 0.467 0.523 0.781 1 0.781h9.844v-12.594zM25 12v12h3v-12h-3z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 739 B |
@ -1,5 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-pointer-o</title>
|
||||
<path d="M13 2c1.645 0 3 1.355 3 3v4.188c0.316-0.114 0.647-0.188 1-0.188 0.767 0 1.467 0.3 2 0.781 0.533-0.481 1.233-0.781 2-0.781 1.395 0 2.578 0.982 2.906 2.281 0.368-0.163 0.762-0.281 1.188-0.281 1.645 0 3 1.355 3 3v7.813c0 4.533-3.654 8.188-8.188 8.188h-1.719c-1.935 0-3.651-0.675-5-1.688l-0.031-0.063-0.063-0.031-8.188-8.094v-0.031c-1.154-1.154-1.154-3.034 0-4.188s3.034-1.154 4.188 0l0.25 0.219 0.656 0.688v-11.813c0-1.645 1.355-3 3-3zM13 4c-0.555 0-1 0.445-1 1v16.625l-4.313-4.313c-0.446-0.446-0.929-0.446-1.375 0s-0.446 0.929 0 1.375l8.094 8c1.051 0.788 2.317 1.313 3.781 1.313h1.719c3.467 0 6.188-2.721 6.188-6.188v-7.813c0-0.555-0.445-1-1-1s-1 0.445-1 1v2h-2.094v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-11c0-0.555-0.445-1-1-1z"></path>
|
||||
<path d="M13 2c1.645 0 3 1.355 3 3v4.188c0.316-0.114 0.647-0.188 1-0.188 0.767 0 1.467 0.3 2 0.781 0.533-0.481 1.233-0.781 2-0.781 1.395 0 2.578 0.982 2.906 2.281 0.368-0.163 0.762-0.281 1.188-0.281 1.645 0 3 1.355 3 3v7.813c0 4.533-3.654 8.188-8.188 8.188h-1.719c-1.935 0-3.651-0.675-5-1.688l-0.031-0.063-0.063-0.031-8.188-8.094v-0.031c-1.154-1.154-1.154-3.034 0-4.188s3.034-1.154 4.188 0l0.25 0.219 0.656 0.688v-11.813c0-1.645 1.355-3 3-3zM13 4c-0.555 0-1 0.445-1 1v16.625l-4.313-4.313c-0.446-0.446-0.929-0.446-1.375 0s-0.446 0.929 0 1.375l8.094 8c1.051 0.788 2.317 1.313 3.781 1.313h1.719c3.467 0 6.188-2.721 6.188-6.188v-7.813c0-0.555-0.445-1-1-1s-1 0.445-1 1v2h-2.094v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-4c0-0.555-0.445-1-1-1s-1 0.445-1 1v4h-2v-11c0-0.555-0.445-1-1-1z"></path>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 957 B |
5
webapp/assets/_new/icons/svgs/hand-right.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-o-right</title>
|
||||
<path d="M14.688 3h0.406c1.616 0 2.908 1.23 2.906 2.656 0 1.336-0.468 2.327-0.938 2.969-0.294 0.401-0.422 0.469-0.625 0.625l-0.188 0.75h10.75c1.645 0 3 1.355 3 3s-1.355 3-3 3h-3.563l-1.656 7.625c-0.296 1.377-1.529 2.375-2.938 2.375h-16.844v-16h5.594l6.781-6.719zM15.406 5.094l-6.406 6.313v12.594h9.844c0.477 0 0.9-0.314 1-0.781l1.781-8.438 0.188-0.781h5.188c0.565 0 1-0.435 1-1s-0.435-1-1-1h-13.344l0.313-1.25 0.594-2.25 0.125-0.406 0.344-0.188s0.179-0.084 0.438-0.438 0.531-0.91 0.531-1.813c0-0.308-0.172-0.481-0.594-0.563zM4 12v12h3v-12h-3z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 718 B |
5
webapp/assets/_new/icons/svgs/hand-stop.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-stop-o</title>
|
||||
<path d="M16 2c1.381 0 2.533 0.97 2.875 2.25 0.351-0.146 0.724-0.25 1.125-0.25 1.645 0 3 1.355 3 3v1.188c0.316-0.114 0.647-0.188 1-0.188 1.645 0 3 1.355 3 3v12c0 3.854-3.146 7-7 7h-4.625c-1.919 0-3.543-0.923-4.719-2.094l-6.781-6.781c-1.163-1.163-1.163-3.087 0-4.25s3.087-1.163 4.25 0l0.875 0.875v-10.75c0-1.645 1.355-3 3-3 0.401 0 0.774 0.104 1.125 0.25 0.342-1.28 1.494-2.25 2.875-2.25zM16 4c-0.565 0-1 0.435-1 1v10h-2v-8c0-0.565-0.435-1-1-1s-1 0.435-1 1v15.594l-1.719-1.719-2.563-2.594c-0.399-0.399-1.039-0.399-1.438 0s-0.399 1.039 0 1.438l6.813 6.75c0.913 0.909 2.009 1.531 3.281 1.531h4.625c2.774 0 5-2.226 5-5v-12c0-0.565-0.435-1-1-1s-1 0.435-1 1v4h-2v-8c0-0.565-0.435-1-1-1s-1 0.435-1 1v8h-2v-10c0-0.565-0.435-1-1-1z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 897 B |
5
webapp/assets/_new/icons/svgs/hand-up.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>hand-o-up</title>
|
||||
<path d="M13 2c1.645 0 3 1.355 3 3v3.563l7.625 1.656c1.377 0.296 2.375 1.529 2.375 2.938v16.844h-16v-5.594l-6.719-6.781-0.281-0.313v-0.406c0-1.616 1.23-2.908 2.656-2.906 1.336 0 2.327 0.468 2.969 0.938 0.401 0.294 0.469 0.422 0.625 0.625l0.75 0.188v-10.75c0-1.645 1.355-3 3-3zM13 4c-0.565 0-1 0.435-1 1v13.344l-1.25-0.313-2.25-0.594-0.406-0.125-0.188-0.344s-0.084-0.179-0.438-0.438-0.91-0.531-1.813-0.531c-0.308-0-0.481 0.172-0.563 0.594l6.313 6.406h12.594v-9.844c0-0.477-0.314-0.9-0.781-1l-8.438-1.781-0.781-0.188v-5.188c0-0.565-0.435-1-1-1zM12 25v3h12v-3h-12z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 734 B |
5
webapp/assets/_new/icons/svgs/headphones.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>headphones</title>
|
||||
<path d="M16 5c6.063 0 11 4.937 11 11v8c0 1.645-1.355 3-3 3h-3v-9h4v-2c0-4.983-4.017-9-9-9s-9 4.017-9 9v2h4v9h-3c-1.645 0-3-1.355-3-3v-8c0-6.063 4.937-11 11-11zM7 20v4c0 0.565 0.435 1 1 1h1v-5h-2zM23 20v5h1c0.565 0 1-0.435 1-1v-4h-2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 407 B |
5
webapp/assets/_new/icons/svgs/heart.svg
Executable file
@ -0,0 +1,5 @@
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<title>heart</title>
|
||||
<path d="M22.5 5c4.136 0 7.5 3.364 7.5 7.5 0 2.59-2.365 4.947-2.466 5.047l-11.534 11.534-11.54-11.54c-0.095-0.094-2.46-2.451-2.46-5.041 0-4.136 3.364-7.5 7.5-7.5 2.892 0 5.327 1.804 6.5 2.854 1.173-1.050 3.608-2.854 6.5-2.854z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 395 B |