diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
new file mode 100644
index 000000000..570fd7ab0
--- /dev/null
+++ b/bitbucket-pipelines.yml
@@ -0,0 +1,21 @@
+image: node:14.17.1
+
+pipelines:
+ branches:
+ VRFS-5232-new_react_frontend:
+ - step:
+ name: Build Production
+ script:
+ - cd jam-ui
+ - npm install
+ - CI=false npm run build
+ artifacts:
+ - jam-ui/build/**
+ - step:
+ name: Deploy to S3
+ deployment: production
+ script:
+ - pipe: atlassian/aws-s3-deploy:1.1.0
+ variables:
+ S3_BUCKET: "jamkazam-ui/prd"
+ LOCAL_PATH: "jam-ui/build"
\ No newline at end of file
diff --git a/jam-ui/.browserslistrc b/jam-ui/.browserslistrc
new file mode 100644
index 000000000..a6cab6d57
--- /dev/null
+++ b/jam-ui/.browserslistrc
@@ -0,0 +1,6 @@
+# Browsers that we support
+
+last 1 version
+> 0.2%
+not op_mini all
+not dead
diff --git a/jam-ui/.env.development.example b/jam-ui/.env.development.example
new file mode 100644
index 000000000..a54034cba
--- /dev/null
+++ b/jam-ui/.env.development.example
@@ -0,0 +1,5 @@
+HOST=beta.jamkazam.local
+PORT=4000
+REACT_APP_ORIGIN=jamkazam.local
+REACT_APP_LEGACY_BASE_URL=http://www.jamkazam.local:3000
+REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
\ No newline at end of file
diff --git a/jam-ui/.env.production b/jam-ui/.env.production
new file mode 100644
index 000000000..14fe4f7a1
--- /dev/null
+++ b/jam-ui/.env.production
@@ -0,0 +1,5 @@
+HOST=beta.jamkazam.com
+PORT=4000
+REACT_APP_ORIGIN=jamkazam.com
+REACT_APP_LEGACY_BASE_URL=https://www.jamkazam.com
+REACT_APP_API_BASE_URL=https://www.jamkazam.com/api
\ No newline at end of file
diff --git a/jam-ui/.eslintrc.json b/jam-ui/.eslintrc.json
new file mode 100644
index 000000000..968b783ae
--- /dev/null
+++ b/jam-ui/.eslintrc.json
@@ -0,0 +1,8 @@
+{
+ "extends": ["react-app", "prettier", "plugin:react/recommended"],
+ "plugins": ["prettier"],
+ "rules": {
+ "prettier/prettier": "error",
+ "react/no-unescaped-entities": false
+ }
+}
diff --git a/jam-ui/.gitignore b/jam-ui/.gitignore
new file mode 100644
index 000000000..785f5ac36
--- /dev/null
+++ b/jam-ui/.gitignore
@@ -0,0 +1,30 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+.cypress.env.json
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+/.idea
+/.vscode
+/test-results
\ No newline at end of file
diff --git a/jam-ui/.gitlab-ci.yml b/jam-ui/.gitlab-ci.yml
new file mode 100644
index 000000000..5277e1ab3
--- /dev/null
+++ b/jam-ui/.gitlab-ci.yml
@@ -0,0 +1,31 @@
+# Using the node alpine image to build the React app
+image: node:alpine
+
+# Announce the URL as per CRA docs
+# https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration
+variables:
+ PUBLIC_URL: /react-falcon
+# Cache node modules - speeds up future builds
+cache:
+ paths:
+ - node_modules
+
+# Name the stages involved in the pipeline
+stages:
+ - deploy
+
+# Job name for gitlab to recognise this results in assets for Gitlab Pages
+# https://docs.gitlab.com/ee/user/project/pages/introduction.html#gitlab-pages-requirements
+pages:
+ stage: deploy
+ script:
+ - npm install # Install all dependencies
+ - npm run build --prod # Build for prod
+ - mv public _public # CRA and gitlab pages both use the public folder. Only do this in a build pipeline.
+ - mv build public # Move build files to public dir for Gitlab Pages
+ - cp public/index.html public/404.html # Required for react router browser history, but helps with https://blog.pshrmn.com/how-single-page-applications-work/
+ artifacts:
+ paths:
+ - public # The built files for Gitlab Pages to serve
+ only:
+ - master # Only run on master branch
diff --git a/jam-ui/.prettierrc b/jam-ui/.prettierrc
new file mode 100644
index 000000000..0981b7cc0
--- /dev/null
+++ b/jam-ui/.prettierrc
@@ -0,0 +1,4 @@
+{
+ "singleQuote": true,
+ "printWidth": 120
+}
diff --git a/jam-ui/README.md b/jam-ui/README.md
new file mode 100644
index 000000000..9ab81eefa
--- /dev/null
+++ b/jam-ui/README.md
@@ -0,0 +1,31 @@
+# JamKazam new react frontend UI/UX
+
+## Running react app
+
+In production this React app is supposed to run on beta.jamkazam.com subdomain which is same origin domain to the production Rails app (www.jamkazam.com). This way we utilize same session based user authentication of Rails web app for authenticating users. (It looks for remember_token session cookie in headers and if it is not availale redirect the user to Rails web app sign in page)
+
+The DOMAIN and PORT running this app is defined in env.production file. This file also has env variables for connecting with Rails app. (When setting up in development you can copy the content of env.development.example in to env.development.local and change them according to your host setup)
+
+HOST=beta.jamkazam.local
+PORT=4000
+REACT_APP_LEGACY_BASE_URL=http://www.jamkazam.local:3000
+REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
+
+## Subdomains setup (development)
+
+You need 2 host records created for React and and Rails app. For example
+
+127.0.0.1 www.jamkazam.local #for Rails app
+127.0.0.1 beta.jamkazam.local #for React app
+
+## Installing npm dependencies
+cd jam-ui
+npm install
+
+## Running the app
+
+cd jam-ui
+npm run start
+
+This will open it in a borwser window at http://beta.jamkazam.local:3000. Of course for it to work you also need Rails (web) app and websocket app (websocket-gateway) running.
+
diff --git a/jam-ui/cypress.env.json b/jam-ui/cypress.env.json
new file mode 100644
index 000000000..d71097f1d
--- /dev/null
+++ b/jam-ui/cypress.env.json
@@ -0,0 +1,6 @@
+{
+ "baseUrl": "http://beta.jamkazam.local:4000",
+ "legacyBaseUrl": "http://www.jamkazam.local:3000",
+ "apiBaseUrl": "http://www.jamkazam.local:3000/api"
+
+}
diff --git a/jam-ui/cypress.json b/jam-ui/cypress.json
new file mode 100644
index 000000000..4a1e6b4ef
--- /dev/null
+++ b/jam-ui/cypress.json
@@ -0,0 +1,7 @@
+{
+ "baseUrl": "http://beta.jamkazam.local:4000",
+ "env": {
+ "legacyBaseUrl": "http://www.jamkazam.local:3000",
+ "apiBaseUrl": "http://www.jamkazam.local:3000/api"
+ }
+}
diff --git a/jam-ui/cypress/fixtures/friend.json b/jam-ui/cypress/fixtures/friend.json
new file mode 100644
index 000000000..034588d65
--- /dev/null
+++ b/jam-ui/cypress/fixtures/friend.json
@@ -0,0 +1,79 @@
+{
+ "id": "1",
+ "first_name": "Test",
+ "last_name": "User1",
+ "name": "Test User1",
+ "city": "Denver",
+ "state": "CO",
+ "country": "US",
+ "location": "Denver, CO",
+ "online": true,
+ "photo_url": null,
+ "musician": true,
+ "gender": "M",
+ "birth_date": null,
+ "friend_count": 1,
+ "liker_count": 0,
+ "follower_count": 0,
+ "following_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "biography": "Biography of Test User1",
+ "favorite_count": 0,
+ "audio_latency": null,
+ "upcoming_session_count": 0,
+ "age": null,
+ "website": "www.testuser1.com",
+ "skill_level": 2,
+ "concert_count": 4,
+ "studio_session_count": 4,
+ "virtual_band": true,
+ "virtual_band_commitment": 2,
+ "traditional_band": true,
+ "traditional_band_commitment": 4,
+ "traditional_band_touring": true,
+ "paid_sessions": true,
+ "paid_sessions_hourly_rate": 10000,
+ "paid_sessions_daily_rate": 200000,
+ "free_sessions": true,
+ "cowriting": true,
+ "cowriting_purpose": 2,
+ "subscribe_email": true,
+ "is_a_teacher": false,
+ "is_a_student": false,
+ "online_presences": [
+ { "id": "e1962204-f652-41b0-84d6-1afd7e9172be", "service_type": "soundcloud", "username": "testuser" },
+ { "id": "005a7c78-db8b-4f72-a51f-d64d579c22b0", "service_type": "reverbnation", "username": "testuser" },
+ { "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" },
+ { "id": "d6ae62b4-e1ce-4cf0-90b7-c64033533261", "service_type": "fandalism", "username": "testuser" },
+ { "id": "c6e85453-0fa9-40d0-9754-8f372d6e0ed3", "service_type": "youtube", "username": "testuser" },
+ { "id": "480ec1ad-ea1d-4990-9c68-d7f9c0174441", "service_type": "facebook", "username": "testuser" },
+ { "id": "232b26d5-c75a-4d65-9013-a07b73c8a7ae", "service_type": "twitter", "username": "testuser" }
+ ],
+ "performance_samples": [],
+ "genres": [
+ { "genre_id": "asian", "player_type": "JamRuby::User", "genre_type": "profile" },
+ { "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "profile" },
+ { "genre_id": "african", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
+ { "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "virtual_band" },
+ { "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "traditional_band" },
+ { "genre_id": "blues", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
+ { "genre_id": "soft rock", "player_type": "JamRuby::User", "genre_type": "free_sessions" },
+ { "genre_id": "celtic", "player_type": "JamRuby::User", "genre_type": "cowriting" },
+ { "genre_id": "tv & movie soundtrack", "player_type": "JamRuby::User", "genre_type": "cowriting" }
+ ],
+ "bands": [],
+ "instruments": [
+ { "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1, "instrument_id": "acoustic guitar" },
+ { "description": "Keyboard", "proficiency_level": 3, "priority": 8, "instrument_id": "keyboard" },
+ { "description": "Ukulele", "proficiency_level": 3, "priority": 11, "instrument_id": "ukulele" },
+ { "description": "Voice", "proficiency_level": 3, "priority": 13, "instrument_id": "voice" },
+ { "description": "Piano", "proficiency_level": 2, "priority": 10, "instrument_id": "piano" }
+ ],
+ "is_friend": true,
+ "is_following": false,
+ "is_liking": false,
+ "pending_friend_request": false,
+ "my_audio_latency": 5,
+ "internet_score": null
+}
diff --git a/jam-ui/cypress/fixtures/notifications.json b/jam-ui/cypress/fixtures/notifications.json
new file mode 100644
index 000000000..cb714ac74
--- /dev/null
+++ b/jam-ui/cypress/fixtures/notifications.json
@@ -0,0 +1,74 @@
+[
+ {
+ "description": "TEXT_MESSAGE",
+ "source_user_id": "27bd4a30-d1b8-4eea-8454-01a104d59381",
+ "target_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
+ "session_id": null,
+ "recording_id": null,
+ "invitation_id": null,
+ "join_request_id": null,
+ "friend_request_id": null,
+ "band_id": null,
+ "band_invitation_id": null,
+ "formatted_msg": "TEXT_MESSAGE",
+ "message": "Hello",
+ "created_at": "2021-10-07T00:09:57.704Z",
+ "lesson_session_id": null,
+ "purpose": null,
+ "source_user": {
+ "name": "Nuwan Chaturanga"
+ },
+ "notification_id": "63fcd878-9a22-4419-9cee-8a51a615da97",
+ "fan_access": null,
+ "musician_access": null,
+ "approval_required": null
+ },
+ {
+ "description": "FRIEND_REQUEST",
+ "source_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
+ "target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
+ "session_id": null,
+ "recording_id": null,
+ "invitation_id": null,
+ "join_request_id": null,
+ "friend_request_id": "7c842904-24f5-4515-8886-0c3d25ee641b",
+ "band_id": null,
+ "band_invitation_id": null,
+ "formatted_msg": "Seth Call has sent you a friend request.",
+ "message": null,
+ "created_at": "2021-10-15T05:36:48.527Z",
+ "lesson_session_id": null,
+ "purpose": null,
+ "source_user": {
+ "name": "Seth Call"
+ },
+ "notification_id": "3364b5f1-8946-46a3-b635-86d89d237849",
+ "fan_access": null,
+ "musician_access": null,
+ "approval_required": null
+ },
+ {
+ "description": "FRIEND_REQUEST_ACCEPTED",
+ "source_user_id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
+ "target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
+ "session_id": null,
+ "recording_id": null,
+ "invitation_id": null,
+ "join_request_id": null,
+ "friend_request_id": null,
+ "band_id": null,
+ "band_invitation_id": null,
+ "formatted_msg": "Peter Walker has accepted your friend request.",
+ "message": null,
+ "created_at": "2021-10-05T12:38:53.134Z",
+ "lesson_session_id": null,
+ "purpose": null,
+ "source_user": {
+ "name": "Peter Walker"
+ },
+ "notification_id": "bb9269f3-721c-48cd-9bf6-bcff72877198",
+ "fan_access": null,
+ "musician_access": null,
+ "approval_required": null
+ }
+]
\ No newline at end of file
diff --git a/jam-ui/cypress/fixtures/people.json b/jam-ui/cypress/fixtures/people.json
new file mode 100644
index 000000000..a83b773dc
--- /dev/null
+++ b/jam-ui/cypress/fixtures/people.json
@@ -0,0 +1,558 @@
+{
+ "musicians": [
+ {
+ "id": "1",
+ "first_name": "Test",
+ "last_name": "User1",
+ "name": "Test User1",
+ "city": "Denver",
+ "state": "CO",
+ "country": "US",
+ "online": true,
+ "musician": true,
+ "photo_url": null,
+ "biography": "Biography of Test User1. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
+ { "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
+ { "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
+ { "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
+ { "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
+ ],
+ "followings": [],
+ "is_friend": true,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 1,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 10,
+ "audio_latency": 5,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "2",
+ "first_name": "Test",
+ "last_name": "User2",
+ "name": "Test User2",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "Biography of Test User2.",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "3",
+ "first_name": "Test",
+ "last_name": "User3",
+ "name": "Test User3",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "4",
+ "first_name": "Test",
+ "last_name": "User4",
+ "name": "Test User4",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "5",
+ "first_name": "Test",
+ "last_name": "User5",
+ "name": "Test User5",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "6",
+ "first_name": "Test",
+ "last_name": "User6",
+ "name": "Test User6",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "7",
+ "first_name": "Test",
+ "last_name": "User7",
+ "name": "Test User7",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "8",
+ "first_name": "Test",
+ "last_name": "User8",
+ "name": "Test User8",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "9",
+ "first_name": "Test",
+ "last_name": "User9",
+ "name": "Test User9",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "10",
+ "first_name": "Test",
+ "last_name": "User10",
+ "name": "Test User10",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+
+ {
+ "id": "11",
+ "first_name": "Test",
+ "last_name": "User11",
+ "name": "Test User11",
+ "city": "Denver",
+ "state": "CO",
+ "country": "US",
+ "online": true,
+ "musician": true,
+ "photo_url": null,
+ "biography": "Biography of Test User1",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
+ { "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
+ { "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
+ { "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
+ { "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
+ ],
+ "followings": [],
+ "is_friend": true,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 1,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 10,
+ "audio_latency": 5,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "12",
+ "first_name": "Test",
+ "last_name": "User12",
+ "name": "Test User12",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "13",
+ "first_name": "Test",
+ "last_name": "User13",
+ "name": "Test User13",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "14",
+ "first_name": "Test",
+ "last_name": "User14",
+ "name": "Test User14",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "15",
+ "first_name": "Test",
+ "last_name": "User15",
+ "name": "Test User15",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "16",
+ "first_name": "Test",
+ "last_name": "User16",
+ "name": "Test User16",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "17",
+ "first_name": "Test",
+ "last_name": "User17",
+ "name": "Test User17",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "18",
+ "first_name": "Test",
+ "last_name": "User18",
+ "name": "Test User18",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "19",
+ "first_name": "Test",
+ "last_name": "User19",
+ "name": "Test User19",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ },
+ {
+ "id": "20",
+ "first_name": "Test",
+ "last_name": "User20",
+ "name": "Test User20",
+ "city": "Austin",
+ "state": "TX",
+ "country": "US",
+ "online": false,
+ "musician": true,
+ "photo_url": null,
+ "biography": "",
+ "full_score": null,
+ "instruments": [
+ { "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
+ ],
+ "followings": [],
+ "is_friend": false,
+ "is_following": false,
+ "pending_friend_request": false,
+ "friend_count": 0,
+ "follow_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "audio_latency": null,
+ "last_active_timestamp": 1629916641
+ }
+ ],
+ "page_count": 2,
+ "my_audio_latency": 5,
+ "filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",
+ "description": "Current Search: Sort = Latency to Me",
+ "is_blank_filter": false
+}
diff --git a/jam-ui/cypress/fixtures/person.json b/jam-ui/cypress/fixtures/person.json
new file mode 100644
index 000000000..22a75e5f2
--- /dev/null
+++ b/jam-ui/cypress/fixtures/person.json
@@ -0,0 +1,81 @@
+{
+ "id": "1",
+ "first_name": "Test",
+ "last_name": "User1",
+ "name": "Test User1",
+ "city": "Denver",
+ "state": "CO",
+ "country": "US",
+ "location": "Denver, CO",
+ "online": true,
+ "photo_url": null,
+ "musician": true,
+ "gender": "M",
+ "birth_date": null,
+ "friend_count": 1,
+ "liker_count": 0,
+ "follower_count": 0,
+ "following_count": 0,
+ "recording_count": 0,
+ "session_count": 0,
+ "biography": "Biography of Test User1",
+ "favorite_count": 0,
+ "audio_latency": null,
+ "upcoming_session_count": 0,
+ "age": null,
+ "website": "www.testuser1.com",
+ "skill_level": 2,
+ "concert_count": 4,
+ "studio_session_count": 4,
+ "virtual_band": true,
+ "virtual_band_commitment": 2,
+ "traditional_band": true,
+ "traditional_band_commitment": 4,
+ "traditional_band_touring": true,
+ "paid_sessions": true,
+ "paid_sessions_hourly_rate": 10000,
+ "paid_sessions_daily_rate": 200000,
+ "free_sessions": true,
+ "cowriting": true,
+ "cowriting_purpose": 2,
+ "subscribe_email": true,
+ "is_a_teacher": false,
+ "is_a_student": false,
+ "online_presences": [
+ { "id": "e1962204-f652-41b0-84d6-1afd7e9172be", "service_type": "soundcloud", "username": "testuser" },
+ { "id": "005a7c78-db8b-4f72-a51f-d64d579c22b0", "service_type": "reverbnation", "username": "testuser" },
+ { "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" }
+ ],
+ "performance_samples": [],
+ "genres": [
+ { "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "profile" },
+ { "genre_id": "blues", "player_type": "JamRuby::User", "genre_type": "free_sessions" }
+ ],
+ "bands": [
+ {
+ "id": "1",
+ "name": "The Band",
+ "admin": true,
+ "photo_url": "",
+ "logo_url": "",
+ "genres": [
+ {
+ "id": 1,
+ "name": "pop"
+ }
+ ]
+ }
+ ],
+ "instruments": [
+ { "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1, "instrument_id": "acoustic guitar" },
+ { "description": "Keyboard", "proficiency_level": 3, "priority": 8, "instrument_id": "keyboard" }
+ ],
+ "is_friend": false,
+ "is_following": false,
+ "is_liking": false,
+ "pending_friend_request": false,
+ "my_audio_latency": 5,
+ "internet_score": null,
+ "created_at_timestamp": 1629917088,
+ "last_active_timestamp": 1629916641
+}
diff --git a/jam-ui/cypress/fixtures/text_messages_page1.json b/jam-ui/cypress/fixtures/text_messages_page1.json
new file mode 100644
index 000000000..69d2638df
--- /dev/null
+++ b/jam-ui/cypress/fixtures/text_messages_page1.json
@@ -0,0 +1,152 @@
+[
+ {
+ "id": "1",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 1",
+ "created_at": "2021-09-20T15:15:15.970Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "2",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 2",
+ "created_at": "2021-09-20T15:14:48.821Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "3",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 3",
+ "created_at": "2021-09-20T15:13:02.367Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "4",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 4",
+ "created_at": "2021-09-18T21:39:58.493Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "5",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 5",
+ "created_at": "2021-09-18T21:37:29.752Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "6",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 6",
+ "created_at": "2021-09-18T21:37:22.319Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "7",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 7",
+ "created_at": "2021-09-18T21:37:12.366Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "8",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 8",
+ "created_at": "2021-09-18T21:23:50.070Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "9",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 9",
+ "created_at": "2021-09-18T21:23:45.271Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "10",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 10",
+ "created_at": "2021-09-18T21:23:07.918Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/jam-ui/cypress/fixtures/text_messages_page2.json b/jam-ui/cypress/fixtures/text_messages_page2.json
new file mode 100644
index 000000000..94233ad00
--- /dev/null
+++ b/jam-ui/cypress/fixtures/text_messages_page2.json
@@ -0,0 +1,152 @@
+[
+ {
+ "id": "11",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 11",
+ "created_at": "2021-09-17T15:15:15.970Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "12",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 12",
+ "created_at": "2021-09-17T15:14:48.821Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "13",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 13",
+ "created_at": "2021-09-17T15:13:02.367Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "14",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 14",
+ "created_at": "2021-09-15T21:39:58.493Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "15",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 15",
+ "created_at": "2021-09-15T21:37:29.752Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "16",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 16",
+ "created_at": "2021-09-15T21:37:22.319Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "17",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 17",
+ "created_at": "2021-09-15T21:37:12.366Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "18",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 18",
+ "created_at": "2021-09-15T21:23:50.070Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "19",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 19",
+ "created_at": "2021-09-15T21:23:45.271Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "20",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 20",
+ "created_at": "2021-09-15T21:23:07.918Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/jam-ui/cypress/fixtures/text_messages_page3.json b/jam-ui/cypress/fixtures/text_messages_page3.json
new file mode 100644
index 000000000..7c7642793
--- /dev/null
+++ b/jam-ui/cypress/fixtures/text_messages_page3.json
@@ -0,0 +1,152 @@
+[
+ {
+ "id": "21",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 21",
+ "created_at": "2021-09-14T15:15:15.970Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "22",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 22",
+ "created_at": "2021-09-14T15:14:48.821Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "23",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 23",
+ "created_at": "2021-09-17T12:13:02.367Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "24",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 24",
+ "created_at": "2021-09-12T21:39:58.493Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "25",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 25",
+ "created_at": "2021-09-12T21:37:29.752Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "26",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 26",
+ "created_at": "2021-09-12T21:37:22.319Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "27",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 27",
+ "created_at": "2021-09-12T21:37:12.366Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "28",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 28",
+ "created_at": "2021-09-12T21:23:50.070Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ },
+ {
+ "id": "29",
+ "source_user_id": "2",
+ "target_user_id": "1",
+ "message": "Test Message 29",
+ "created_at": "2021-09-12T21:23:45.271Z",
+ "source_user": {
+ "id": "2",
+ "name": "Test User2"
+ },
+ "target_user": {
+ "id": "1",
+ "name": "Test User1"
+ }
+ },
+ {
+ "id": "30",
+ "source_user_id": "1",
+ "target_user_id": "2",
+ "message": "Test Message 30",
+ "created_at": "2021-09-12T21:23:07.918Z",
+ "source_user": {
+ "id": "1",
+ "name": "Test User1"
+ },
+ "target_user": {
+ "id": "2",
+ "name": "Test User2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/jam-ui/cypress/integration/friends/friends-page.spec.js b/jam-ui/cypress/integration/friends/friends-page.spec.js
new file mode 100644
index 000000000..f31d069f9
--- /dev/null
+++ b/jam-ui/cypress/integration/friends/friends-page.spec.js
@@ -0,0 +1,367 @@
+///
`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover() {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n color: inherit;\n text-decoration: none;\n\n @include hover() {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // Disable auto-hiding scrollbar in IE & legacy Edge to avoid overlap,\n // making it impossible to interact with the content\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `