From 4136985ced996e06f2c7737e592bd03f60867e80 Mon Sep 17 00:00:00 2001 From: Andrew Mendelsohn Date: Mon, 26 Feb 2024 13:46:08 -0800 Subject: [PATCH 1/6] [PAY-2524, PAY-2525, PAY-2526] Add collections_containing_track field to tracks (#7708) Co-authored-by: Dharit Tantiviramanond --- .../0056_playlists_containing_track.sql | 18 ++ .../entity_manager/test_playlist_tracks.py | 210 +++++++++++++++++- .../integration_tests/utils.py | 3 + packages/discovery-provider/scripts/lint.sh | 4 + .../src/models/tracks/track.py | 4 + .../tasks/entity_manager/entities/playlist.py | 39 +++- 6 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 packages/discovery-provider/ddl/migrations/0056_playlists_containing_track.sql create mode 100755 packages/discovery-provider/scripts/lint.sh diff --git a/packages/discovery-provider/ddl/migrations/0056_playlists_containing_track.sql b/packages/discovery-provider/ddl/migrations/0056_playlists_containing_track.sql new file mode 100644 index 00000000000..e21ece18eb1 --- /dev/null +++ b/packages/discovery-provider/ddl/migrations/0056_playlists_containing_track.sql @@ -0,0 +1,18 @@ +BEGIN; + +ALTER TABLE tracks +ADD column IF NOT EXISTS playlists_containing_track INTEGER[] NOT NULL DEFAULT '{}'; + +UPDATE tracks SET playlists_containing_track = subquery.playlists_containing_track FROM ( + SELECT + CAST(jsonb_array_elements(playlist_contents->'track_ids')->>'track' AS INTEGER) AS track_id, + ARRAY_AGG(playlist_id) AS playlists_containing_track + FROM playlists + WHERE playlist_id IS NOT NULL + GROUP BY track_id +) AS subquery +WHERE tracks.track_id = subquery.track_id + AND subquery.track_id IS NOT NULL +AND tracks.track_id IS NOT NULL; + +COMMIT; diff --git a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_playlist_tracks.py b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_playlist_tracks.py index ca1e3c93b67..4450eaeae32 100644 --- a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_playlist_tracks.py +++ b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_playlist_tracks.py @@ -10,14 +10,17 @@ from integration_tests.utils import populate_mock_db from src.challenges.challenge_event_bus import ChallengeEventBus, setup_challenge_bus from src.models.playlists.playlist_track import PlaylistTrack +from src.models.tracks.track import Track from src.tasks.entity_manager.entity_manager import entity_manager_update from src.tasks.entity_manager.utils import PLAYLIST_ID_OFFSET from src.utils.db_session import get_db logger = logging.getLogger(__name__) -# Insert Playlist with two new tracks and check that a notification is created for the track owners +PLAYLIST_ID = PLAYLIST_ID_OFFSET + 1 + now = datetime.now() + entities = { "users": [ {"user_id": 1, "handle": "user-1", "wallet": "user1wallet"}, @@ -119,7 +122,7 @@ def get_events_side_effect(_, tx_receipt): { "args": AttributeDict( { - "_entityId": PLAYLIST_ID_OFFSET + 1, + "_entityId": PLAYLIST_ID, "_entityType": "Playlist", "_userId": 1, "_action": "Create", @@ -153,6 +156,27 @@ def test_create_playlist(app, mocker): for id in [30, 40]: assert not any([relation.track_id == id for relation in relations]) + assert session.query(Track).filter( + Track.track_id == 10 + ).first().playlists_containing_track == [PLAYLIST_ID] + assert session.query(Track).filter( + Track.track_id == 20 + ).first().playlists_containing_track == [PLAYLIST_ID] + assert ( + session.query(Track) + .filter(Track.track_id == 30) + .first() + .playlists_containing_track + == [] + ) + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) + # Add tracks to a playlist add_tracks_to_playlist_tx_receipts = { @@ -194,6 +218,27 @@ def test_add_tracks_to_playlist(app, mocker): for id in [10, 40]: assert not any([relation.track_id == id for relation in relations]) + assert ( + session.query(Track) + .filter(Track.track_id == 10) + .first() + .playlists_containing_track + == [] + ) + assert session.query(Track).filter( + Track.track_id == 20 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert session.query(Track).filter( + Track.track_id == 30 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) + # Remove a track from an playlist remove_track_from_playlist_tx_receipts = { @@ -258,6 +303,31 @@ def test_remove_track_from_playlist(app, mocker): for id in [10, 40]: assert not any([relation.track_id == id for relation in relations]) + assert ( + session.query(Track) + .filter(Track.track_id == 10) + .first() + .playlists_containing_track + == [] + ) + assert ( + session.query(Track) + .filter(Track.track_id == 20) + .first() + .playlists_containing_track + == [] + ) + assert session.query(Track).filter( + Track.track_id == 30 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) + # Remove a track from a playlist and then restore it restore_removed_track_to_playlist_tx_receipts = { @@ -337,6 +407,27 @@ def test_restore_removed_track_to_playlist(app, mocker): ] ) + assert ( + session.query(Track) + .filter(Track.track_id == 10) + .first() + .playlists_containing_track + == [] + ) + assert session.query(Track).filter( + Track.track_id == 20 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert session.query(Track).filter( + Track.track_id == 30 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) + # Create a playlist then reorder the tracks reorder_playlist_tracks_tx_receipts = { @@ -391,3 +482,118 @@ def test_reorder_playlist_tracks(app, mocker): assert any([relation.track_id == id for relation in relations]) for id in [10, 40]: assert not any([relation.track_id == id for relation in relations]) + + assert ( + session.query(Track) + .filter(Track.track_id == 10) + .first() + .playlists_containing_track + == [] + ) + assert session.query(Track).filter( + Track.track_id == 20 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert session.query(Track).filter( + Track.track_id == 30 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) + + +# Add track to multiple playlists +add_tracks_to_multiple_playlists_tx_receipts = { + "UpdatePlaylistTracklistUpdate": [ + { + "args": AttributeDict( + { + "_entityId": PLAYLIST_ID_OFFSET, + "_entityType": "Playlist", + "_userId": 1, + "_action": "Update", + "_metadata": f'{{"cid": "PlaylistTracklistUpdate", "data": {json.dumps(test_metadata["PlaylistTracklistUpdate"])}, "timestamp": {datetime.timestamp(now)}}}', + "_signer": "user1wallet", + } + ) + } + ], + "CreatePlaylist": [ + { + "args": AttributeDict( + { + "_entityId": PLAYLIST_ID_OFFSET + 1, + "_entityType": "Playlist", + "_userId": 1, + "_action": "Create", + "_metadata": f'{{"cid": "QmCreatePlaylist1", "data": {json.dumps(test_metadata["PlaylistToCreate"])}}}', + "_signer": "user1wallet", + } + ) + }, + ], +} + + +def test_add_track_to_multiple_playlists(app, mocker): + db, update_task, entity_manager_txs = setup_db( + app, mocker, entities, add_tracks_to_multiple_playlists_tx_receipts + ) + + with db.scoped_session() as session: + entity_manager_update( + update_task, + session, + entity_manager_txs, + block_number=0, + block_timestamp=1585336422, + block_hash=hex(0), + ) + playlist_1_relations: List[PlaylistTrack] = ( + session.query(PlaylistTrack) + .filter(PlaylistTrack.playlist_id == PLAYLIST_ID_OFFSET) + .all() + ) + assert len(playlist_1_relations) == 2 + for id in [20, 30]: + assert any([relation.track_id == id for relation in playlist_1_relations]) + for id in [10, 40]: + assert not any( + [relation.track_id == id for relation in playlist_1_relations] + ) + + playlist_2_relations: List[PlaylistTrack] = ( + session.query(PlaylistTrack) + .filter(PlaylistTrack.playlist_id == PLAYLIST_ID_OFFSET + 1) + .all() + ) + assert len(playlist_2_relations) == 2 + for id in [10, 20]: + assert any([relation.track_id == id for relation in playlist_2_relations]) + for id in [30, 40]: + assert not any( + [relation.track_id == id for relation in playlist_2_relations] + ) + + assert session.query(Track).filter( + Track.track_id == 10 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET + 1] + assert session.query(Track).filter( + Track.track_id == 20 + ).first().playlists_containing_track == [ + PLAYLIST_ID_OFFSET, + PLAYLIST_ID_OFFSET + 1, + ] + assert session.query(Track).filter( + Track.track_id == 30 + ).first().playlists_containing_track == [PLAYLIST_ID_OFFSET] + assert ( + session.query(Track) + .filter(Track.track_id == 40) + .first() + .playlists_containing_track + == [] + ) diff --git a/packages/discovery-provider/integration_tests/utils.py b/packages/discovery-provider/integration_tests/utils.py index f6962dcca5c..9b0287738f2 100644 --- a/packages/discovery-provider/integration_tests/utils.py +++ b/packages/discovery-provider/integration_tests/utils.py @@ -231,6 +231,9 @@ def populate_mock_db(db, entities, block_offset=None): is_playlist_upload=track_meta.get("is_playlist_upload", False), track_cid=track_meta.get("track_cid", None), ai_attribution_user_id=track_meta.get("ai_attribution_user_id", None), + playlists_containing_track=track_meta.get( + "playlists_containing_track", [] + ), ) session.add(track) for i, track_price_history_meta in enumerate(track_price_history): diff --git a/packages/discovery-provider/scripts/lint.sh b/packages/discovery-provider/scripts/lint.sh new file mode 100755 index 00000000000..4a7513bd01a --- /dev/null +++ b/packages/discovery-provider/scripts/lint.sh @@ -0,0 +1,4 @@ +black . +flake8 . +mypy . +isort . diff --git a/packages/discovery-provider/src/models/tracks/track.py b/packages/discovery-provider/src/models/tracks/track.py index 8314609d970..da814534abb 100644 --- a/packages/discovery-provider/src/models/tracks/track.py +++ b/packages/discovery-provider/src/models/tracks/track.py @@ -1,4 +1,5 @@ from sqlalchemy import ( + ARRAY, Boolean, Column, DateTime, @@ -86,6 +87,9 @@ class Track(Base, RepresentableMixin): is_download_gated = Column(Boolean, nullable=False, server_default=text("false")) download_conditions = Column(JSONB(True)) is_playlist_upload = Column(Boolean, nullable=False, server_default=text("false")) + playlists_containing_track = Column( + ARRAY(Integer()), server_default="ARRAY[]::INTEGER[]" + ) ai_attribution_user_id = Column(Integer, nullable=True) block1 = relationship( # type: ignore diff --git a/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py b/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py index 49c09e5cd5d..63c700fea20 100644 --- a/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py +++ b/packages/discovery-provider/src/tasks/entity_manager/entities/playlist.py @@ -6,6 +6,7 @@ from src.models.playlists.playlist import Playlist from src.models.playlists.playlist_route import PlaylistRoute from src.models.playlists.playlist_track import PlaylistTrack +from src.models.tracks.track import Track from src.tasks.entity_manager.utils import ( CHARACTER_LIMIT_DESCRIPTION, PLAYLIST_ID_OFFSET, @@ -131,10 +132,22 @@ def update_playlist_tracks(params: ManageEntityParameters, playlist_record: Play ) # delete relations that previously existed but are not in the updated list - for relation in existing_playlist_tracks: - if relation.track_id not in updated_track_ids: - relation.is_removed = True - relation.updated_at = params.block_datetime + for playlist_track in existing_playlist_tracks: + if playlist_track.track_id not in updated_track_ids: + playlist_track.is_removed = True + playlist_track.updated_at = params.block_datetime + track = ( + session.query(Track) + .filter(Track.track_id == playlist_track.track_id) + .first() + ) + if track: + track.updated_at = params.block_datetime + track.playlists_containing_track = [ + collection_id + for collection_id in (track.playlists_containing_track or []) + if collection_id != playlist["playlist_id"] + ] for track_id in updated_track_ids: # add row for each track that is not already in the table @@ -148,10 +161,28 @@ def update_playlist_tracks(params: ManageEntityParameters, playlist_record: Play ) # upsert to handle duplicates session.merge(new_playlist_track) + track = session.query(Track).filter(Track.track_id == track_id).first() + if track: + track.updated_at = params.block_datetime + track.playlists_containing_track = list( + set( + (track.playlists_containing_track or []) + + [playlist["playlist_id"]] + ) + ) elif existing_tracks[track_id].is_removed: # recover deleted relation (track was previously removed then re-added) existing_tracks[track_id].is_removed = False existing_tracks[track_id].updated_at = params.block_datetime + track = session.query(Track).filter(Track.track_id == track_id).first() + if track: + track.updated_at = params.block_datetime + track.playlists_containing_track = list( + set( + (track.playlists_containing_track or []) + + [playlist["playlist_id"]] + ) + ) params.logger.info( f"playlists.py | Updated playlist tracks for {playlist['playlist_id']}" From 6230eca84ee3a80299e17c65b8310e0ea50006da Mon Sep 17 00:00:00 2001 From: Reed <3893871+dharit-tan@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:46:23 -0500 Subject: [PATCH 2/6] [PAY-2528] is_stream_gated + stream_conditions on Playlist (#7717) Co-authored-by: amendelsohn --- .../ddl/migrations/0057_playlist_stream_conditions.sql | 8 ++++++++ .../discovery-provider/src/models/playlists/playlist.py | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 packages/discovery-provider/ddl/migrations/0057_playlist_stream_conditions.sql diff --git a/packages/discovery-provider/ddl/migrations/0057_playlist_stream_conditions.sql b/packages/discovery-provider/ddl/migrations/0057_playlist_stream_conditions.sql new file mode 100644 index 00000000000..88bdc3dc5c9 --- /dev/null +++ b/packages/discovery-provider/ddl/migrations/0057_playlist_stream_conditions.sql @@ -0,0 +1,8 @@ + +begin; + +-- add new columns +alter table playlists add column if not exists is_stream_gated boolean not null default false; +alter table playlists add column if not exists stream_conditions jsonb; + +commit; diff --git a/packages/discovery-provider/src/models/playlists/playlist.py b/packages/discovery-provider/src/models/playlists/playlist.py index 086359a1884..3ee90ea7937 100644 --- a/packages/discovery-provider/src/models/playlists/playlist.py +++ b/packages/discovery-provider/src/models/playlists/playlist.py @@ -34,6 +34,8 @@ class Playlist(Base, RepresentableMixin): playlist_image_multihash = Column(String) is_current = Column(Boolean, primary_key=True, nullable=False) is_delete = Column(Boolean, nullable=False) + is_stream_gated = Column(Boolean, nullable=False, server_default=text("false")) + stream_conditions = Column(JSONB(True)) description = Column(String) created_at = Column(DateTime, nullable=False, index=True) upc = Column(String) From beb054b7d8c4b3de8d3af1522eadc402f709761e Mon Sep 17 00:00:00 2001 From: Danny Date: Mon, 26 Feb 2024 16:03:50 -0800 Subject: [PATCH 3/6] Remove all usage of PROTOCOL_DIR env var (#7714) --- .circleci/src/jobs/release-audius-sdk.yml | 1 - dev-tools/README.md | 2 - dev-tools/audius-cloud | 172 ------------------ dev-tools/audius-cmd | 6 +- dev-tools/audius-compose | 4 +- dev-tools/setup-dev.sh | 12 -- dev-tools/setup.sh | 23 +-- packages/libs/scripts/release.sh | 2 +- .../anchor/audius-data/scripts/build-dev.sh | 4 +- .../scripts/install-dev-dependencies.sh | 4 +- .../anchor/audius-data/scripts/parse-tx.sh | 7 +- .../anchor/audius-data/scripts/seed-tx.sh | 4 +- 12 files changed, 25 insertions(+), 216 deletions(-) delete mode 100755 dev-tools/audius-cloud delete mode 100644 dev-tools/setup-dev.sh diff --git a/.circleci/src/jobs/release-audius-sdk.yml b/.circleci/src/jobs/release-audius-sdk.yml index ed725793c7a..db403101d55 100644 --- a/.circleci/src/jobs/release-audius-sdk.yml +++ b/.circleci/src/jobs/release-audius-sdk.yml @@ -28,5 +28,4 @@ steps: - run: name: Release command: | - export PROTOCOL_DIR=$PWD packages/libs/scripts/release.sh << parameters.sdk-release-commit >> << parameters.sdk-release-version >> << parameters.sdk-release-preid >> diff --git a/dev-tools/README.md b/dev-tools/README.md index 5342ab411b6..060d4f71e05 100644 --- a/dev-tools/README.md +++ b/dev-tools/README.md @@ -38,14 +38,12 @@ audius-compose connect Use these CLI tools for Audius dev. Currently there are 3 tools available: -- `audius-cloud`: create a pre-configured GCP VM for dev - `audius-compose`: start, stop, and manage Audius service containers - `audius-cmd`: perform user actions, e.g. `create-user`, `upload-track`, `repost-playlist` View all available commands for any of these tools with `--help`, e.g. ```bash -> audius-cloud --help > audius-compose --help > audius-cmd --help ``` diff --git a/dev-tools/audius-cloud b/dev-tools/audius-cloud deleted file mode 100755 index 07a092674a0..00000000000 --- a/dev-tools/audius-cloud +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import time -import uuid - -import click - - -def wait_for_instance(name): - while True: - try: - subprocess.check_output( - ["gcloud", "compute", "ssh", f"audius@{name}", "--", "exit 0"] - ) - break - except subprocess.CalledProcessError: - time.sleep(1) - - -@click.group() -def cli(): - pass - - -@cli.command() -@click.pass_context -def create_image(ctx): - name = f"audius-cloud-{uuid.uuid4()}" - - ctx.invoke( - create_raw, - name=name, - gcloud_args=[ - "--boot-disk-size=64G", - "--no-boot-disk-auto-delete", - "--boot-disk-type=pd-ssd", - ], - ) - - ctx.invoke(delete, gcloud_args=[name]) - - subprocess.run( - [ - "gcloud", - "compute", - "images", - "create", - "audius-protocol-dev", - f"--source-disk={name}", - ], - check=True, - ) - - subprocess.run( - [ - "gcloud", - "compute", - "disks", - "delete", - "--quiet", # disable prompt - name, - ], - check=True, - ) - - -@cli.command(context_settings=dict(ignore_unknown_options=True)) -@click.argument("name") -@click.argument("gcloud-args", nargs=-1, type=click.UNPROCESSED) -def create(name, gcloud_args): - subprocess.run( - [ - "gcloud", - "compute", - "instances", - "create", - "--machine-type=n2-custom-6-24576", - "--boot-disk-size=256G", - "--boot-disk-type=pd-ssd", - name, - *gcloud_args, - ], - check=True, - ) - - -@cli.command(context_settings=dict(ignore_unknown_options=True)) -@click.argument("name") -@click.argument("gcloud-args", nargs=-1, type=click.UNPROCESSED) -def create_raw(name, gcloud_args): - click.secho(f"Creating instance {name}", fg="green") - - subprocess.run( - [ - "gcloud", - "compute", - "instances", - "create", - "--machine-type=n2-custom-6-24576", - "--boot-disk-size=256G", - "--boot-disk-type=pd-ssd", - name, - *gcloud_args, - ], - check=True, - ) - - click.secho(f"Waiting for instance {name} to be ready", fg="green") - - wait_for_instance(name) - - click.secho("Setting up audius-protocol on instance", fg="green") - - subprocess.run( - [ - "gcloud", - "compute", - "ssh", - f"audius@{name}", - "--", - "curl 'https://raw.githubusercontent.com/AudiusProject/audius-protocol/main/dev-tools/setup.sh' | bash", - ], - check=True, - ) - - subprocess.run( - [ - "gcloud", - "compute", - "ssh", - f"audius@{name}", - "--", - "cd ~/audius-protocol && PROTOCOL_DIR=$PWD ~/.local/bin/audius-compose build && docker compose pull", - ], - check=True, - ) - - -@cli.command(context_settings=dict(ignore_unknown_options=True)) -@click.argument("gcloud-args", nargs=-1, type=click.UNPROCESSED) -def delete(gcloud_args): - subprocess.run( - [ - "gcloud", - "compute", - "instances", - "delete", - "--quiet", # disable prompt - *gcloud_args, - ], - check=True, - ) - - -@cli.command(context_settings=dict(ignore_unknown_options=True)) -@click.argument("gcloud-args", nargs=-1, type=click.UNPROCESSED) -def describe(gcloud_args): - subprocess.run( - [ - "gcloud", - "compute", - "instances", - "describe", - *gcloud_args, - ], - check=True, - ) - - -if __name__ == "__main__": - cli() diff --git a/dev-tools/audius-cmd b/dev-tools/audius-cmd index 993dd69b24a..0c461c6f847 100755 --- a/dev-tools/audius-cmd +++ b/dev-tools/audius-cmd @@ -1,10 +1,8 @@ #!/usr/bin/env bash set -e -if [[ "$PROTOCOL_DIR" != "" ]]; then - cd $PROTOCOL_DIR/dev-tools/compose -fi -export COMPOSE_PROJECT_NAME=$(basename $PROTOCOL_DIR) +cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"/compose # audius-protocol/dev-tools/compose +export COMPOSE_PROJECT_NAME='audius-protocol' if [[ "$1" == "test" ]]; then docker compose exec audius-cmd npm test diff --git a/dev-tools/audius-compose b/dev-tools/audius-compose index 6285d3a2faf..1131b0defb2 100755 --- a/dev-tools/audius-compose +++ b/dev-tools/audius-compose @@ -170,7 +170,6 @@ def generate_env( @click.group() @click.option( "--protocol-dir", - envvar="PROTOCOL_DIR", type=click.Path( exists=True, file_okay=False, resolve_path=True, path_type=pathlib.Path ), @@ -178,7 +177,7 @@ def generate_env( @click.pass_context def cli(ctx, protocol_dir): if protocol_dir is None: - protocol_dir = pathlib.Path.cwd().resolve() + protocol_dir = pathlib.Path(os.path.dirname(os.path.realpath(__file__))).resolve() while ( protocol_dir.name != "" and not (protocol_dir / "dev-tools/compose/docker-compose.yml").exists() @@ -186,7 +185,6 @@ def cli(ctx, protocol_dir): protocol_dir = protocol_dir.parent if protocol_dir.name == "": raise click.ClickException("Unable to find protocol dir") - click.confirm(f'Using protocol dir "{protocol_dir}"', default=True, abort=True) ctx.obj = protocol_dir diff --git a/dev-tools/setup-dev.sh b/dev-tools/setup-dev.sh deleted file mode 100644 index 4567390bacc..00000000000 --- a/dev-tools/setup-dev.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -# This script is meant for debian/ubuntu systems; support for other os might be added in the future - -# Install nvm and required node versions -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash -source ~/.nvm/nvm.sh -[ -f "~/.bashrc" ] && source ~/.bashrc -for dir in contracts eth-contracts packages/identity-service packages/libs; do - cd "$PROTOCOL_DIR/$dir" - nvm install -done diff --git a/dev-tools/setup.sh b/dev-tools/setup.sh index 08d44a46e22..8b3573ce61e 100755 --- a/dev-tools/setup.sh +++ b/dev-tools/setup.sh @@ -47,6 +47,10 @@ debian | ubuntu) # Increase file watchers echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + + # add script directories to path, avoiding duplication + grep -q "export PATH=$HOME/.local/bin:" ~/.profile || echo "export PATH=$HOME/.local/bin:\$PATH" >>~/.profile + ;; *) if ! command -v docker &>/dev/null; then @@ -72,22 +76,15 @@ debian | ubuntu) esac if [[ "${BASH_SOURCE[0]}" == "" ]]; then - export PROTOCOL_DIR="${PROTOCOL_DIR:-$HOME/audius-protocol}" - git clone https://github.com/AudiusProject/audius-protocol.git "$PROTOCOL_DIR" + protocol_dir="$HOME/audius-protocol" + git clone https://github.com/AudiusProject/audius-protocol.git "$protocol_dir" else - export PROTOCOL_DIR="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" + protocol_dir="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" fi -python3 -m pip install -r "$PROTOCOL_DIR/dev-tools/requirements.txt" +python3 -m pip install -r "$protocol_dir/dev-tools/requirements.txt" mkdir -p "$HOME/.local/bin" -ln -sf "$PROTOCOL_DIR/dev-tools/audius-compose" "$HOME/.local/bin/audius-compose" -ln -sf "$PROTOCOL_DIR/dev-tools/audius-cloud" "$HOME/.local/bin/audius-cloud" -ln -sf "$PROTOCOL_DIR/dev-tools/audius-cmd" "$HOME/.local/bin/audius-cmd" - -# Add env vars to .profile, avoiding duplication -grep -q PROTOCOL_DIR ~/.profile || echo "export PROTOCOL_DIR=$PROTOCOL_DIR" >>~/.profile -grep -q "export PATH=$HOME/.local/bin:" ~/.profile || echo "export PATH=$HOME/.local/bin:\$PATH" >>~/.profile - -[[ "$AUDIUS_DEV" != "false" ]] && . "$PROTOCOL_DIR/dev-tools/setup-dev.sh" || true +ln -sf "$protocol_dir/dev-tools/audius-compose" "$HOME/.local/bin/audius-compose" +ln -sf "$protocol_dir/dev-tools/audius-cmd" "$HOME/.local/bin/audius-cmd" diff --git a/packages/libs/scripts/release.sh b/packages/libs/scripts/release.sh index 24a7a8f0cb0..cfbb3f27ef5 100755 --- a/packages/libs/scripts/release.sh +++ b/packages/libs/scripts/release.sh @@ -146,7 +146,7 @@ function cleanup () { # configuration STUB=@audius/sdk -cd ${PROTOCOL_DIR}/packages/libs +cd "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" # audius-protocol/packages/libs # pull in main git-reset diff --git a/solana-programs/anchor/audius-data/scripts/build-dev.sh b/solana-programs/anchor/audius-data/scripts/build-dev.sh index 3df43f3ef52..efdbcc6c23b 100755 --- a/solana-programs/anchor/audius-data/scripts/build-dev.sh +++ b/solana-programs/anchor/audius-data/scripts/build-dev.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euxo pipefail -cd $PROTOCOL_DIR/solana-programs/anchor/audius-data +cd "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" # audius-protocol/solana-programs/anchor/audius-data # Replace program ID with solana pubkey generated from anchor build cur_address=$(grep -Po '(?<=declare_id!\(").*(?=")' programs/audius-data/src/lib.rs) echo "Building and deploying audius_data program with anchor CLI..." @@ -11,4 +11,4 @@ sed -i "s/$cur_address/$AUDIUS_DATA_PROGRAM_ID/g" Anchor.toml sed -i "s/$cur_address/$AUDIUS_DATA_PROGRAM_ID/g" programs/audius-data/src/lib.rs anchor build -echo "audius-data program built. Start solana test validator and deploy by running, in separate windows, npm run localnet-up and npm run deploy-dev." \ No newline at end of file +echo "audius-data program built. Start solana test validator and deploy by running, in separate windows, npm run localnet-up and npm run deploy-dev." diff --git a/solana-programs/anchor/audius-data/scripts/install-dev-dependencies.sh b/solana-programs/anchor/audius-data/scripts/install-dev-dependencies.sh index 585c7adb025..d33dc06fd6d 100755 --- a/solana-programs/anchor/audius-data/scripts/install-dev-dependencies.sh +++ b/solana-programs/anchor/audius-data/scripts/install-dev-dependencies.sh @@ -5,7 +5,7 @@ RUST_VERSION=1.59.0 # rustc version 1.59.0 (9d1b2106e 2022-02-23) SOLANA_CLI_VERSION=v1.9.13 ANCHOR_CLI_VERSION=v0.24.1 -cd $PROTOCOL_DIR/solana-programs/anchor/audius-data +cd "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" # audius-protocol/solana-programs/anchor/audius-data echo "Installing dev deps for anchor audius-data development..." # install rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y @@ -38,4 +38,4 @@ npm install # init solana keypair solana-keygen new --no-bip39-passphrase --force -o "$HOME/.config/solana/id.json" -echo "Installed deps for anchor development." \ No newline at end of file +echo "Installed deps for anchor development." diff --git a/solana-programs/anchor/audius-data/scripts/parse-tx.sh b/solana-programs/anchor/audius-data/scripts/parse-tx.sh index 8de755d5351..650ad963572 100755 --- a/solana-programs/anchor/audius-data/scripts/parse-tx.sh +++ b/solana-programs/anchor/audius-data/scripts/parse-tx.sh @@ -1,11 +1,14 @@ #!/usr/bin/env bash set -euo pipefail -TX_PARSER_DIR="$PROTOCOL_DIR/packages/discovery-provider/solana-tx-parser" + +protocol_dir="$(readlink -f "${BASH_SOURCE[0]}" | sed -nr 's/(.*)\/solana-programs\/anchor.*/\1/p')" +TX_PARSER_DIR="$protocol_dir/packages/discovery-provider/solana-tx-parser" + AUDIUS_DATA_PROGRAM_ID=$(solana-keygen pubkey $PWD/target/deploy/audius_data-keypair.json) echo "Installing parser deps if needed..." cd "$TX_PARSER_DIR" && python3.9 -m pip install -r requirements.txt echo "Running parser with tx hash "$@"... If no tx hash is provided, parser will default to all tx for program ID $AUDIUS_DATA_PROGRAM_ID" -TX_HASH="$@" python3.9 tx_parser.py \ No newline at end of file +TX_HASH="$@" python3.9 tx_parser.py diff --git a/solana-programs/anchor/audius-data/scripts/seed-tx.sh b/solana-programs/anchor/audius-data/scripts/seed-tx.sh index 3ce4786d687..50ab6e50a1c 100755 --- a/solana-programs/anchor/audius-data/scripts/seed-tx.sh +++ b/solana-programs/anchor/audius-data/scripts/seed-tx.sh @@ -4,7 +4,7 @@ set -x # TODO - MOVE OUT OF SHELL SCRIPT ASAP -ANCHOR_PROGRAM_DIR="$PROTOCOL_DIR/solana-programs/anchor/audius-data" +ANCHOR_PROGRAM_DIR="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")" # audius-protocol/solana-programs/anchor/audius-data OWNER_KEYPAIR_PATH="$HOME/.config/solana/id.json" ADMIN_AUTHORITY_KEYPAIR_PATH="$PWD/adminAuthorityKeypair.json" ADMIN_ACCOUNT_KEYPAIR_PATH="$PWD/adminAccountKeypair.json" @@ -106,4 +106,4 @@ yarn run ts-node cli/main.ts -f deletePlaylist \ echo "Successfully seeded tx:" -solana transaction-history "$AUDIUS_DATA_PROGRAM_ID" \ No newline at end of file +solana transaction-history "$AUDIUS_DATA_PROGRAM_ID" From 5a3498cdfb6b0fd32f7e6c15a8a664b6068781fb Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Mon, 26 Feb 2024 16:55:51 -0800 Subject: [PATCH 4/6] [DVRL-11] Move client history requests to sdk (#7679) --- .../audius-api-client/AudiusAPIClient.ts | 40 ----------------- .../services/audius-backend/AudiusBackend.ts | 8 +++- .../common/src/services/explore/Explore.ts | 30 ++++++++++--- packages/common/src/store/upload/reducer.ts | 3 +- packages/common/src/store/upload/selectors.ts | 1 + packages/mobile/src/services/explore.ts | 4 +- .../store/pages/history/lineups/sagas.ts | 45 +++++++++++++------ .../common/store/smart-collection/sagas.ts | 42 +++++++++++------ packages/web/src/services/explore.ts | 4 +- 9 files changed, 98 insertions(+), 79 deletions(-) diff --git a/packages/common/src/services/audius-api-client/AudiusAPIClient.ts b/packages/common/src/services/audius-api-client/AudiusAPIClient.ts index 2150b869e57..7dc0a93a9e0 100644 --- a/packages/common/src/services/audius-api-client/AudiusAPIClient.ts +++ b/packages/common/src/services/audius-api-client/AudiusAPIClient.ts @@ -101,7 +101,6 @@ const FULL_ENDPOINT_MAP = { getRemixing: (trackId: OpaqueID) => `/tracks/${trackId}/remixing`, searchFull: `/search/full`, searchAutocomplete: `/search/autocomplete`, - getUserTrackHistory: (userId: OpaqueID) => `/users/${userId}/history/tracks`, getUserSupporter: (userId: OpaqueID, supporterUserId: OpaqueID) => `/users/${userId}/supporters/${supporterUserId}`, getUserSupporting: (userId: OpaqueID, supporterUserId: OpaqueID) => @@ -431,14 +430,6 @@ export type GetSocialFeedArgs = QueryParams & { type GetSocialFeedResponse = {} -type GetUserTrackHistoryArgs = { - userId: ID - currentUserId: Nullable - limit?: number - offset?: number - sortMethod?: string -} - type GetReactionArgs = { reactedToIds: string[] } @@ -1627,37 +1618,6 @@ export class AudiusAPIClient { return response.data } - async getUserTrackHistory({ - currentUserId, - userId, - offset = 0, - limit = 100, - sortMethod - }: GetUserTrackHistoryArgs) { - const encodedUserId = this._encodeOrThrow(userId) - const encodedCurrentUserId = encodeHashId(currentUserId) - this._assertInitialized() - const params = { - user_id: encodedCurrentUserId || undefined, - limit, - offset, - sort_method: sortMethod - } - - const response = await this._getResponse>( - FULL_ENDPOINT_MAP.getUserTrackHistory(encodedUserId), - params - ) - - if (!response) return [] - - const adapted = response.data.map(({ item, ...props }) => ({ - timestamp: props.timestamp, - track: adapter.makeTrack(item as APITrack) - })) - return adapted - } - async getUserSupporter({ currentUserId, userId, diff --git a/packages/common/src/services/audius-backend/AudiusBackend.ts b/packages/common/src/services/audius-backend/AudiusBackend.ts index 62767a19a3f..b23a4ce9306 100644 --- a/packages/common/src/services/audius-backend/AudiusBackend.ts +++ b/packages/common/src/services/audius-backend/AudiusBackend.ts @@ -1792,7 +1792,13 @@ export const audiusBackend = ({ } } - function getDiscoveryEntityType({ type, is_album }: { type: string, is_album?: boolean}) { + function getDiscoveryEntityType({ + type, + is_album + }: { + type: string + is_album?: boolean + }) { if (type === 'track') { return Entity.Track } else if (is_album === true) { diff --git a/packages/common/src/services/explore/Explore.ts b/packages/common/src/services/explore/Explore.ts index 82c14df7012..3de0910b5ac 100644 --- a/packages/common/src/services/explore/Explore.ts +++ b/packages/common/src/services/explore/Explore.ts @@ -1,3 +1,5 @@ +import { AudiusSdk } from '@audius/sdk' + import { Collection, FeedFilter, @@ -8,6 +10,7 @@ import { } from '../../models' import { encodeHashId, removeNullable } from '../../utils' import { + APIActivityV2, APIPlaylist, APITrack, AudiusAPIClient, @@ -28,15 +31,18 @@ type TopUserListen = { type ExploreConfig = { audiusBackendInstance: AudiusBackend apiClient: AudiusAPIClient + audiusSdk: () => Promise } export class Explore { audiusBackendInstance: AudiusBackend apiClient: AudiusAPIClient + audiusSdk: () => Promise constructor(config: ExploreConfig) { this.audiusBackendInstance = config.audiusBackendInstance this.apiClient = config.apiClient + this.audiusSdk = config.audiusSdk } /** TRACKS ENDPOINTS */ @@ -83,6 +89,7 @@ export class Explore { } async getFeedNotListenedTo(currentUserId: ID, limit = 25) { + const sdk = await this.audiusSdk() try { const lineupItems = (await this.apiClient.getSocialFeed({ offset: 0, @@ -97,18 +104,27 @@ export class Explore { const tracks = lineupItems.filter( (lineupItem): lineupItem is UserTrack => 'track_id' in lineupItem ) + const { data, signature } = + await this.audiusBackendInstance.signDiscoveryNodeRequest() + const history = await sdk.full.users.getUsersTrackHistory({ + id: encodeHashId(currentUserId), + encodedDataMessage: data, + encodedDataSignature: signature, + limit: 100 + }) + const activityData = history.data as APIActivityV2[] + const listenedToTracks = activityData + .map(responseAdapter.makeActivity) + .filter(removeNullable) as UserTrackMetadata[] // Imperfect solution. Ideally we use an endpoint that gives us true/false // if a user has listened to a passed in array of tracks. - const history = await this.apiClient.getUserTrackHistory({ - currentUserId, - userId: currentUserId, - limit: 50 - }) - const listens = history.map((item) => item.track?.track_id) + const listenendToTrackIds = listenedToTracks.map( + (track) => track.track_id + ) const notListenedToTracks = tracks.filter( - (track) => !listens[track.track_id] + (track) => !listenendToTrackIds[track.track_id] ) return notListenedToTracks.slice(0, limit) } catch (e) { diff --git a/packages/common/src/store/upload/reducer.ts b/packages/common/src/store/upload/reducer.ts index a34dc0809fc..b5e89e91c8a 100644 --- a/packages/common/src/store/upload/reducer.ts +++ b/packages/common/src/store/upload/reducer.ts @@ -1,5 +1,7 @@ import { cloneDeep } from 'lodash' +import { StemUploadWithFile } from '~/models' + import { TOGGLE_MULTI_TRACK_NOTIFICATION, UPLOAD_TRACKS_REQUESTED, @@ -16,7 +18,6 @@ import { uploadSingleTrackFailed } from './actions' import { ProgressStatus, UploadState, UploadTrack } from './types' -import { StemUploadWithFile } from '~/models' const initialState: UploadState = { openMultiTrackNotification: true, diff --git a/packages/common/src/store/upload/selectors.ts b/packages/common/src/store/upload/selectors.ts index 88bc31bac99..b59d85692bc 100644 --- a/packages/common/src/store/upload/selectors.ts +++ b/packages/common/src/store/upload/selectors.ts @@ -1,6 +1,7 @@ import { floor, clamp } from 'lodash' import { CommonState } from '../commonStore' + import { ProgressStatus } from './types' export const getStems = (state: CommonState) => state.upload.stems diff --git a/packages/mobile/src/services/explore.ts b/packages/mobile/src/services/explore.ts index e68de46e04e..c417bceb50f 100644 --- a/packages/mobile/src/services/explore.ts +++ b/packages/mobile/src/services/explore.ts @@ -2,8 +2,10 @@ import { Explore } from '@audius/common/services' import { apiClient } from './audius-api-client' import { audiusBackendInstance } from './audius-backend-instance' +import { audiusSdk } from './sdk/audius-sdk' export const explore = new Explore({ audiusBackendInstance, - apiClient + apiClient, + audiusSdk }) diff --git a/packages/web/src/common/store/pages/history/lineups/sagas.ts b/packages/web/src/common/store/pages/history/lineups/sagas.ts index 35990ca614d..425c947aff8 100644 --- a/packages/web/src/common/store/pages/history/lineups/sagas.ts +++ b/packages/web/src/common/store/pages/history/lineups/sagas.ts @@ -1,10 +1,15 @@ import { LineupEntry, Track, UserTrackMetadata } from '@audius/common/models' +import { responseAdapter } from '@audius/common/services' import { accountSelectors, getContext, historyPageTracksLineupActions as tracksActions } from '@audius/common/store' -import { removeNullable } from '@audius/common/utils' +import { + decodeHashId, + encodeHashId, + removeNullable +} from '@audius/common/utils' import { keyBy } from 'lodash' import { call, select } from 'typed-redux-saga' @@ -17,28 +22,40 @@ const { prefix: PREFIX } = tracksActions function* getHistoryTracks() { yield* waitForRead() - const apiClient = yield* getContext('apiClient') + const audiusSdk = yield* getContext('audiusSdk') + const audiusBackendInstance = yield* getContext('audiusBackendInstance') + const sdk = yield* call(audiusSdk) try { const currentUserId = yield* select(getUserId) if (!currentUserId) return [] - const activity = yield* call([apiClient, apiClient.getUserTrackHistory], { - currentUserId, - userId: currentUserId, - limit: 100 - }) + const { data, signature } = yield* call([ + audiusBackendInstance, + audiusBackendInstance.signDiscoveryNodeRequest + ]) + const activity = yield* call( + [sdk.full.users, sdk.full.users.getUsersTrackHistory], + { + id: encodeHashId(currentUserId), + encodedDataMessage: data, + encodedDataSignature: signature, + limit: 100 + } + ) + const activityData = activity.data + if (!activityData) return [] - const activityTracks: UserTrackMetadata[] = activity - .map((a) => a.track) - .filter(removeNullable) + const tracks = activityData + .map(responseAdapter.makeActivity) + .filter(removeNullable) as UserTrackMetadata[] - const processedTracks = yield* call(processAndCacheTracks, activityTracks) + const processedTracks = yield* call(processAndCacheTracks, tracks) const processedTracksMap = keyBy(processedTracks, 'track_id') const lineupTracks: Track[] = [] - activity.forEach((activity) => { - const trackMetadata = activity.track - ? processedTracksMap[activity.track.track_id] + activityData.forEach((activity) => { + const trackMetadata = activity.item + ? processedTracksMap[decodeHashId(activity.item.id)!] : null // Prevent history for invalid tracks from getting into the lineup. if (trackMetadata) { diff --git a/packages/web/src/common/store/smart-collection/sagas.ts b/packages/web/src/common/store/smart-collection/sagas.ts index c1afbe04cb0..605ce837c5b 100644 --- a/packages/web/src/common/store/smart-collection/sagas.ts +++ b/packages/web/src/common/store/smart-collection/sagas.ts @@ -4,6 +4,7 @@ import { UserTrackMetadata, UserTrack } from '@audius/common/models' +import { responseAdapter } from '@audius/common/services' import { accountSelectors, smartCollectionPageActions, @@ -11,7 +12,7 @@ import { collectionPageActions, getContext } from '@audius/common/store' -import { removeNullable } from '@audius/common/utils' +import { encodeHashId, removeNullable } from '@audius/common/utils' import { takeEvery, put, call, select } from 'typed-redux-saga' import { processAndCacheTracks } from 'common/store/cache/tracks/utils' @@ -38,22 +39,34 @@ const { getUserId } = accountSelectors const COLLECTIONS_LIMIT = 25 function* fetchHeavyRotation() { + yield* waitForRead() + const currentUserId = yield* select(getUserId) - const apiClient = yield* getContext('apiClient') - const userListeningHistoryMostListenedByUser = yield* call( - [apiClient, apiClient.getUserTrackHistory], + const audiusSdk = yield* getContext('audiusSdk') + const audiusBackendInstance = yield* getContext('audiusBackendInstance') + const sdk = yield* call(audiusSdk) + + const { data, signature } = yield* call([ + audiusBackendInstance, + audiusBackendInstance.signDiscoveryNodeRequest + ]) + if (!currentUserId) return { ...HEAVY_ROTATION } + const activity = yield* call( + [sdk.full.users, sdk.full.users.getUsersTrackHistory], { - userId: currentUserId!, - currentUserId, - limit: 20, - offset: 0, - sortMethod: 'most_listens_by_user' + id: encodeHashId(currentUserId), + encodedDataMessage: data, + encodedDataSignature: signature, + sortMethod: 'most_listens_by_user', + limit: 20 } ) + const activityData = activity.data + if (!activityData) return { ...HEAVY_ROTATION } - const mostListenedTracks = userListeningHistoryMostListenedByUser - .map((listeningHistoryWithTrack) => listeningHistoryWithTrack.track) - .filter(removeNullable) + const mostListenedTracks = activityData + .map(responseAdapter.makeActivity) + .filter(removeNullable) as UserTrackMetadata[] const users = yield* call( retrieveUsers, @@ -80,8 +93,8 @@ function* fetchHeavyRotation() { } function* fetchBestNewReleases() { - const explore = yield* getContext('explore') yield* waitForRead() + const explore = yield* getContext('explore') const currentUserId = yield* select(getUserId) if (currentUserId == null) { return @@ -110,6 +123,7 @@ function* fetchBestNewReleases() { } function* fetchUnderTheRadar() { + yield* waitForRead() const explore = yield* getContext('explore') const currentUserId = yield* select(getUserId) if (currentUserId == null) { @@ -183,8 +197,8 @@ function* fetchFeelingLucky() { } function* fetchRemixables() { - const explore = yield* getContext('explore') yield* waitForRead() + const explore = yield* getContext('explore') const currentUserId = yield* select(getUserId) if (currentUserId == null) { return diff --git a/packages/web/src/services/explore.ts b/packages/web/src/services/explore.ts index e0e351fec49..3e0cec5541f 100644 --- a/packages/web/src/services/explore.ts +++ b/packages/web/src/services/explore.ts @@ -2,8 +2,10 @@ import { Explore } from '@audius/common/services' import { apiClient } from './audius-api-client' import { audiusBackendInstance } from './audius-backend/audius-backend-instance' +import { audiusSdk } from './audius-sdk' export const explore = new Explore({ audiusBackendInstance, - apiClient + apiClient, + audiusSdk }) From 3e1b544766d68a9cf5212e120b23024935f8b160 Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Mon, 26 Feb 2024 17:28:07 -0800 Subject: [PATCH 5/6] Add env linter (#7697) --- .circleci/config.yml | 1 + package-lock.json | 24 +++++++++++ packages/ddex/webapp/client/.env.stage.local | 2 +- packages/ddex/webapp/client/package.json | 4 +- packages/dotenv-linter/.gitignore | 1 + packages/dotenv-linter/package.json | 19 +++++++++ packages/embed/.env.dev | 8 ++-- packages/embed/.env.prod | 12 +++--- packages/embed/.env.stage | 10 ++--- packages/embed/package.json | 5 ++- packages/embed/src/components/app.jsx | 4 +- .../pausedpopover/ListenOnAudiusCTA.jsx | 13 +++--- .../components/pausedpopover/PrimaryLabel.jsx | 3 +- .../src/components/track/TrackPlayerCard.jsx | 3 +- .../components/track/TrackPlayerCompact.jsx | 3 +- .../src/components/track/TrackPlayerTiny.jsx | 3 +- packages/mobile/.env.dev | 2 +- packages/mobile/.env.prod | 2 +- packages/mobile/.env.stage | 2 +- packages/mobile/package.json | 4 +- packages/web/env/.env.prod | 2 +- packages/web/env/.env.ssr | 2 +- packages/web/env/.env.stage | 2 +- packages/web/package.json | 4 +- protocol-dashboard/.env.prod | 42 +++++++++---------- protocol-dashboard/.env.stage | 42 +++++++++---------- protocol-dashboard/package.json | 4 +- scripts/postinstall.sh | 6 +-- turbo.json | 10 +++-- 29 files changed, 143 insertions(+), 96 deletions(-) create mode 100644 packages/dotenv-linter/.gitignore create mode 100644 packages/dotenv-linter/package.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e341c2e581..d2a231ff1c0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,6 +85,7 @@ jobs: - ./packages/ddex/webapp/node_modules - ./packages/ddex/publisher/node_modules - ./packages/trpc-server/node_modules + - ./packages/dotenv-linter/bin generate-release-branch: working_directory: ~/audius-protocol diff --git a/package-lock.json b/package-lock.json index c5d60f006a0..04ebfe49e94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -900,6 +900,10 @@ "resolved": "packages/ddex/webapp/server", "link": true }, + "node_modules/@audius/dotenv-linter": { + "resolved": "packages/dotenv-linter", + "link": true + }, "node_modules/@audius/fetch-nft": { "version": "0.1.8", "license": "ISC", @@ -122695,6 +122699,7 @@ "web3": "4.3.0" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "@vitejs/plugin-react-swc": "3.5.0", @@ -139044,6 +139049,21 @@ "version": "0.0.22", "license": "MIT" }, + "packages/dotenv-lint": { + "name": "@audius/dotenv-lint", + "version": "1.5.67", + "extraneous": true, + "license": "ISC" + }, + "packages/dotenv-linter": { + "name": "@audius/dotenv-linter", + "version": "1.5.67", + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "dotenv-linter": "bin/dotenv-linter" + } + }, "packages/embed": { "version": "1.5.68", "dependencies": { @@ -139080,6 +139100,7 @@ "web3-utils": "1.3.4" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@cloudflare/kv-asset-handler": "^0.3.0", "@vitejs/plugin-react": "4.1.0", "env-cmd": "10.1.0", @@ -146171,6 +146192,7 @@ "zod-formik-adapter": "1.2.0" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@babel/core": "7.21.0", "@babel/plugin-transform-export-namespace-from": "7.23.4", "@babel/plugin-transform-react-jsx": "7.21.0", @@ -155075,6 +155097,7 @@ "zustand": "4.4.3" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@electron/notarize": "2.2.0", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@pinata/sdk": "1.1.13", @@ -157578,6 +157601,7 @@ "vite": "4.4.9" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@openzeppelin/test-helpers": "0.5.6", "@pinata/sdk": "1.1.13", "@tanstack/eslint-plugin-query": "5.0.5", diff --git a/packages/ddex/webapp/client/.env.stage.local b/packages/ddex/webapp/client/.env.stage.local index c9b1f41c3bd..72dbdfa1df7 100644 --- a/packages/ddex/webapp/client/.env.stage.local +++ b/packages/ddex/webapp/client/.env.stage.local @@ -1,4 +1,4 @@ # For testing staging locally (ie, npm run stage) +VITE_DDEX_KEY_OVERRIDE=49d5e13d355709b615b7cce7369174fb240b6b39 VITE_ENV_OVERRIDE=stage VITE_NODE_URL_OVERRIDE=https://audius-ddex.staging.audius.co -VITE_DDEX_KEY_OVERRIDE=49d5e13d355709b615b7cce7369174fb240b6b39 diff --git a/packages/ddex/webapp/client/package.json b/packages/ddex/webapp/client/package.json index e610a2559a9..92fdac5f6d9 100644 --- a/packages/ddex/webapp/client/package.json +++ b/packages/ddex/webapp/client/package.json @@ -14,8 +14,9 @@ "serve": "vite preview", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:fix": "eslint --fix src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:env": "dotenv-linter", "typecheck": "tsc", - "verify": "concurrently \"npm:typecheck\" \"npm:lint\"" + "verify": "concurrently \"npm:typecheck\" \"npm:lint\" \"npm:lint:env\"" }, "dependencies": { "@audius/harmony": "*", @@ -43,6 +44,7 @@ "web3": "4.3.0" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "@vitejs/plugin-react-swc": "3.5.0", diff --git a/packages/dotenv-linter/.gitignore b/packages/dotenv-linter/.gitignore new file mode 100644 index 00000000000..efa6632a634 --- /dev/null +++ b/packages/dotenv-linter/.gitignore @@ -0,0 +1 @@ +bin/* \ No newline at end of file diff --git a/packages/dotenv-linter/package.json b/packages/dotenv-linter/package.json new file mode 100644 index 00000000000..a2cc5ffb81f --- /dev/null +++ b/packages/dotenv-linter/package.json @@ -0,0 +1,19 @@ +{ + "name": "@audius/dotenv-linter", + "private": true, + "version": "1.5.67", + "description": "dotenv lint", + "author": "Audius", + "homepage": "https://github.com/AudiusProject/audius-protocol#readme", + "license": "Apache-2.0", + "bin": { + "dotenv-linter": "bin/dotenv-linter" + }, + "scripts": { + "install": "BINDIR=./bin curl -sSfL https://raw.githubusercontent.com/dotenv-linter/dotenv-linter/v3.3.0/install.sh | sh -s", + "rebuild": "npm rebuild --ignore-scripts" + }, + "bugs": { + "url": "https://github.com/AudiusProject/audius-protocol/issues" + } +} diff --git a/packages/embed/.env.dev b/packages/embed/.env.dev index deb807b1f15..1c8e1a7e69f 100644 --- a/packages/embed/.env.dev +++ b/packages/embed/.env.dev @@ -1,8 +1,8 @@ -VITE_AUDIUS_SCHEME=http -VITE_AMPLITUDE_KEY=72a58ce4ad1f9bafcba0b92bedb6c33d VITE_AMPLITUDE_API_PROXY=gain.audius.co +VITE_AMPLITUDE_KEY=72a58ce4ad1f9bafcba0b92bedb6c33d +VITE_AUDIUS_SCHEME=http VITE_ENVIRONMENT=development -VITE_IDENTITY_ENDPOINT=https://identityservice.staging.audius.co VITE_HOSTNAME_REDIRECT=redirect.staging.audius.co +VITE_IDENTITY_ENDPOINT=https://identityservice.staging.audius.co VITE_OPEN_SEA_API_KEY=639642f563224bf894274aaf77dd1ce7 -VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com \ No newline at end of file +VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com diff --git a/packages/embed/.env.prod b/packages/embed/.env.prod index 4daa1714770..c5c7397e650 100644 --- a/packages/embed/.env.prod +++ b/packages/embed/.env.prod @@ -1,10 +1,10 @@ -VITE_AUDIUS_SCHEME=https -VITE_IDENTITY_ENDPOINT=https://identityservice.audius.co -VITE_AMPLITUDE_KEY=86760558b8bb1b3aae61656efd4ddacb VITE_AMPLITUDE_API_PROXY=gain.audius.co -VITE_HOSTNAME=audius.co -VITE_HOSTNAME_REDIRECT=redirect.audius.co +VITE_AMPLITUDE_KEY=86760558b8bb1b3aae61656efd4ddacb +VITE_AUDIUS_SCHEME=https VITE_ENVIRONMENT=production VITE_GA_MEASUREMENT_ID=G-V6N1ZTVGS5 +VITE_HOSTNAME=audius.co +VITE_HOSTNAME_REDIRECT=redirect.audius.co +VITE_IDENTITY_ENDPOINT=https://identityservice.audius.co VITE_OPEN_SEA_API_KEY=639642f563224bf894274aaf77dd1ce7 -VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com \ No newline at end of file +VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com diff --git a/packages/embed/.env.stage b/packages/embed/.env.stage index f1a8cda7d88..f1022b4f8bd 100644 --- a/packages/embed/.env.stage +++ b/packages/embed/.env.stage @@ -1,9 +1,9 @@ -VITE_AUDIUS_SCHEME=https -VITE_IDENTITY_ENDPOINT=https://identityservice.staging.audius.co -VITE_AMPLITUDE_KEY=72a58ce4ad1f9bafcba0b92bedb6c33d VITE_AMPLITUDE_API_PROXY=gain.audius.co +VITE_AMPLITUDE_KEY=72a58ce4ad1f9bafcba0b92bedb6c33d +VITE_AUDIUS_SCHEME=https +VITE_ENVIRONMENT=staging VITE_HOSTNAME=staging.audius.co VITE_HOSTNAME_REDIRECT=redirect.staging.audius.co -VITE_ENVIRONMENT=staging +VITE_IDENTITY_ENDPOINT=https://identityservice.staging.audius.co VITE_OPEN_SEA_API_KEY=639642f563224bf894274aaf77dd1ce7 -VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com \ No newline at end of file +VITE_SOLANA_RPC_ENDPOINT=https://audius-fe.rpcpool.com diff --git a/packages/embed/package.json b/packages/embed/package.json index c0f146b7cdb..ddf986cb30e 100644 --- a/packages/embed/package.json +++ b/packages/embed/package.json @@ -12,9 +12,12 @@ "build:prod": "env-cmd -f .env.prod npm run build && npm run build-api && mv build build-production", "build-api": "webpack --config src/api/webpack.config.js -o build/api.js", "lint:fix": "eslint --cache --fix --ext=js,jsx,ts,tsx src", - "lint": "eslint --cache --ext=js,jsx,ts,tsx src" + "lint": "eslint --cache --ext=js,jsx,ts,tsx src", + "lint:env": "dotenv-linter", + "verify": "concurrently \"npm:lint:fix\" \"npm:lint:env\"" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@cloudflare/kv-asset-handler": "^0.3.0", "@vitejs/plugin-react": "4.1.0", "env-cmd": "10.1.0", diff --git a/packages/embed/src/components/app.jsx b/packages/embed/src/components/app.jsx index 0e241b7d7c2..c40e615c035 100644 --- a/packages/embed/src/components/app.jsx +++ b/packages/embed/src/components/app.jsx @@ -436,8 +436,8 @@ const App = (props) => { const artworkClickURL = tracksResponse?.permalink || collectionsResponse?.permalink ? stripLeadingSlash( - tracksResponse?.permalink || collectionsResponse?.permalink - ) + tracksResponse?.permalink || collectionsResponse?.permalink + ) : null const listenOnAudiusURL = artworkClickURL const flavor = requestState.playerFlavor diff --git a/packages/embed/src/components/pausedpopover/ListenOnAudiusCTA.jsx b/packages/embed/src/components/pausedpopover/ListenOnAudiusCTA.jsx index fbf3015c40b..87b56142432 100644 --- a/packages/embed/src/components/pausedpopover/ListenOnAudiusCTA.jsx +++ b/packages/embed/src/components/pausedpopover/ListenOnAudiusCTA.jsx @@ -12,8 +12,7 @@ const ListenOnAudiusCTA = ({ audiusURL, streamConditions }) => { const onClick = () => { window.open(getCopyableLink(audiusURL), '_blank') } - const isPurchaseable = - streamConditions && 'usdc_purchase' in streamConditions + const isPurchaseable = streamConditions && 'usdc_purchase' in streamConditions return ( diff --git a/packages/embed/src/components/pausedpopover/PrimaryLabel.jsx b/packages/embed/src/components/pausedpopover/PrimaryLabel.jsx index 412e5f36131..8f7607db263 100644 --- a/packages/embed/src/components/pausedpopover/PrimaryLabel.jsx +++ b/packages/embed/src/components/pausedpopover/PrimaryLabel.jsx @@ -6,8 +6,7 @@ const messages = { } const PrimaryLabel = ({ streamConditions }) => { - const isPurchaseable = - streamConditions && 'usdc_purchase' in streamConditions + const isPurchaseable = streamConditions && 'usdc_purchase' in streamConditions return ( diff --git a/packages/embed/src/components/track/TrackPlayerCard.jsx b/packages/embed/src/components/track/TrackPlayerCard.jsx index 7cdaa309463..72b9e797cca 100644 --- a/packages/embed/src/components/track/TrackPlayerCard.jsx +++ b/packages/embed/src/components/track/TrackPlayerCard.jsx @@ -58,8 +58,7 @@ const TrackPlayerCard = ({ } setArtworkWrapperStyle(newStyle) }, [height, width]) - const isPurchaseable = - streamConditions && 'usdc_purchase' in streamConditions + const isPurchaseable = streamConditions && 'usdc_purchase' in streamConditions return ( { - const isPurchaseable = - streamConditions && 'usdc_purchase' in streamConditions + const isPurchaseable = streamConditions && 'usdc_purchase' in streamConditions return (
diff --git a/packages/mobile/.env.dev b/packages/mobile/.env.dev index 2e6583494e5..f409384bff8 100644 --- a/packages/mobile/.env.dev +++ b/packages/mobile/.env.dev @@ -1,2 +1,2 @@ ENVIRONMENT=development -TIKTOK_APP_ID=awa9re2w7ec3xrn6 \ No newline at end of file +TIKTOK_APP_ID=awa9re2w7ec3xrn6 diff --git a/packages/mobile/.env.prod b/packages/mobile/.env.prod index 9569ac667cd..3d7c45f9128 100644 --- a/packages/mobile/.env.prod +++ b/packages/mobile/.env.prod @@ -1,2 +1,2 @@ ENVIRONMENT=production -TIKTOK_APP_ID=awa9re2w7ec3xrn6 \ No newline at end of file +TIKTOK_APP_ID=awa9re2w7ec3xrn6 diff --git a/packages/mobile/.env.stage b/packages/mobile/.env.stage index 64e3354e662..a55251e778c 100644 --- a/packages/mobile/.env.stage +++ b/packages/mobile/.env.stage @@ -1,2 +1,2 @@ ENVIRONMENT=staging -TIKTOK_APP_ID=awa9re2w7ec3xrn6 \ No newline at end of file +TIKTOK_APP_ID=awa9re2w7ec3xrn6 diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 394b4920deb..6a329ee9de1 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -35,6 +35,7 @@ "jetifier": "jetifier", "lint:fix": "eslint --cache --fix --ext=js,jsx,ts,tsx src", "lint": "eslint --cache --ext=js,jsx,ts,tsx src", + "lint:env": "dotenv-linter", "react-devtools": "npx react-devtools", "redux-devtools": "redux-devtools --port=8000 --open", "start:e2e": "RN_E2E=true react-native start", @@ -49,7 +50,7 @@ "typecheck": "tsc", "upgrade": "react-native upgrade", "upload:ios": "xcrun altool --upload-app -type ios --file build/AudiusReactNative.ipa --username $APPLE_ID --password $APPLE_ID_PASSWORD", - "verify": "concurrently \"npm:typecheck\" \"npm:lint:fix\"" + "verify": "concurrently \"npm:typecheck\" \"npm:lint:fix\" \"npm:lint:env\"" }, "dependencies": { "@amplitude/react-native": "2.17.2", @@ -189,6 +190,7 @@ "ios-deploy": "1.11.4" }, "devDependencies": { + "@audius/dotenv-linter": "*", "@babel/core": "7.21.0", "@babel/plugin-transform-export-namespace-from": "7.23.4", "@babel/plugin-transform-react-jsx": "7.21.0", diff --git a/packages/web/env/.env.prod b/packages/web/env/.env.prod index d1c237fb22d..51658e3db3e 100644 --- a/packages/web/env/.env.prod +++ b/packages/web/env/.env.prod @@ -1,2 +1,2 @@ VITE_ENVIRONMENT=production -VITE_PORT=3002 \ No newline at end of file +VITE_PORT=3002 diff --git a/packages/web/env/.env.ssr b/packages/web/env/.env.ssr index 3c64dd8810e..e85da653f88 100644 --- a/packages/web/env/.env.ssr +++ b/packages/web/env/.env.ssr @@ -1 +1 @@ -VITE_SSR=true \ No newline at end of file +VITE_SSR=true diff --git a/packages/web/env/.env.stage b/packages/web/env/.env.stage index c0e3f19f82f..0ecb481060c 100644 --- a/packages/web/env/.env.stage +++ b/packages/web/env/.env.stage @@ -1,2 +1,2 @@ VITE_ENVIRONMENT=staging -VITE_PORT=3001 \ No newline at end of file +VITE_PORT=3001 diff --git a/packages/web/package.json b/packages/web/package.json index 1e8f16bd265..4de3c777e94 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "build": "npm run write-sha && NODE_OPTIONS=--max-old-space-size=8192 env-cmd ./env/.env.git vite build", "lint:fix": "eslint --cache --fix --ext=js,jsx,ts,tsx src", "lint": "eslint --cache --ext=js,jsx,ts,tsx src", + "lint:env": "dotenv-linter env", "prebuild": "npm run publish-scripts", "preview:prod": "npm run build:prod && vite preview --outDir build-production", "publish-scripts": "./scripts/publishScripts.sh", @@ -34,7 +35,7 @@ "test": "vitest", "typecheck:watch": "tsc --watch", "typecheck": "tsc", - "verify": "concurrently \"npm:typecheck\" \"npm:lint:fix\" \"npm:stylelint:fix\"", + "verify": "concurrently \"npm:typecheck\" \"npm:lint:fix\" \"npm:stylelint:fix\" \"npm:lint:env\"", "worker-ssr:dev": "npx wrangler dev --env test --config ./src/ssr/wrangler.toml", "worker:dev": "npx wrangler dev --env test", "write-sha": "./scripts/writeSHA.sh", @@ -201,6 +202,7 @@ "extends": null }, "devDependencies": { + "@audius/dotenv-linter": "*", "@electron/notarize": "2.2.0", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@pinata/sdk": "1.1.13", diff --git a/protocol-dashboard/.env.prod b/protocol-dashboard/.env.prod index 6999dfdde28..1708d10c59c 100644 --- a/protocol-dashboard/.env.prod +++ b/protocol-dashboard/.env.prod @@ -1,31 +1,27 @@ +VITE_AUDIUS_NETWORK_ID=31524 +VITE_AUDIUS_URL=https://audius.co +VITE_CLAIMABLE_TOKEN_PDA=5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx +VITE_CLAIMABLE_TOKEN_PROGRAM_ADDRESS=Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ +VITE_CLAIM_DISTRIBUTION_CONTRACT_ADDRESS=0x683c19E621A0F107a291fdAB38f80179809d61B5 +VITE_ENTITY_MANAGER_ADDRESS=0x1Cd8a543596D499B9b6E7a6eC15ECd2B7857Fd64 VITE_ENVIRONMENT=production +VITE_ETH_NETWORK_ID=1 +VITE_ETH_OWNER_WALLET=0xC7310a03e930DD659E15305ed7e1F5Df0F0426C5 +VITE_ETH_PROVIDER_URL=https://eth-mainnet.alchemyapi.io/v2/hELYSivAlDc8LV29Mw_LumSdCZ4HQEge,https://eth-mainnet.alchemyapi.io/v2/Iy15fLTuL7wfRNqhMWZOyusLiqG2LGHJ,https://eth-mainnet.alchemyapi.io/v2/dKWeJWSF5lz5y_o0lQNVR2BfcmYglClO,https://eth-mainnet.alchemyapi.io/v2/W--Uss7AqotdfKao0PH6aTQa9bOG4osc VITE_ETH_REGISTRY_ADDRESS=0xd976d3b4f4e22a238c1A736b6612D22f17b6f64C VITE_ETH_TOKEN_ADDRESS=0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998 -VITE_ETH_PROVIDER_URL=https://eth-mainnet.alchemyapi.io/v2/hELYSivAlDc8LV29Mw_LumSdCZ4HQEge,https://eth-mainnet.alchemyapi.io/v2/Iy15fLTuL7wfRNqhMWZOyusLiqG2LGHJ,https://eth-mainnet.alchemyapi.io/v2/dKWeJWSF5lz5y_o0lQNVR2BfcmYglClO,https://eth-mainnet.alchemyapi.io/v2/W--Uss7AqotdfKao0PH6aTQa9bOG4osc -VITE_ETH_OWNER_WALLET=0xC7310a03e930DD659E15305ed7e1F5Df0F0426C5 -VITE_QUERY_PROPOSAL_START_BLOCK = 11818009 - -VITE_ETH_NETWORK_ID=1 -VITE_AUDIUS_NETWORK_ID=31524 - -VITE_AUDIUS_URL=https://audius.co -VITE_GQL_URI=https://gateway.thegraph.com/api/372f3681a94e4a45867d46cf59423e22/subgraphs/id/0x819fd65026848d710fe40d8c0439f1220e069398-0 VITE_GQL_BACKUP_URI=https://api.thegraph.com/subgraphs/name/audius-infra/audius-network-mainnet -VITE_REGISTRY_ADDRESS=0xC611C82150b56E6e4Ec5973AcAbA8835Dd0d75A2 -VITE_ENTITY_MANAGER_ADDRESS=0x1Cd8a543596D499B9b6E7a6eC15ECd2B7857Fd64 - +VITE_GQL_URI=https://gateway.thegraph.com/api/372f3681a94e4a45867d46cf59423e22/subgraphs/id/0x819fd65026848d710fe40d8c0439f1220e069398-0 VITE_IDENTITY_SERVICE_ENDPOINT=https://identityservice.audius.co - -VITE_WORMHOLE_ADDRESS=0x6E7a1F7339bbB62b23D44797b63e4258d283E095 -VITE_CLAIM_DISTRIBUTION_CONTRACT_ADDRESS=0x683c19E621A0F107a291fdAB38f80179809d61B5 -VITE_SOLANA_WEB3_CLUSTER=mainnet-beta -VITE_SOLANA_CLUSTER_ENDPOINT=https://solana-mainnet.g.alchemy.com/v2/fCWqhO4QJ7X8XqXvi213Mzclmn-30-ie -VITE_WAUDIO_MINT_ADDRESS=9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM -VITE_USDC_MINT_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -VITE_SOLANA_TOKEN_PROGRAM_ADDRESS=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA -VITE_CLAIMABLE_TOKEN_PDA=5ZiE3vAkrdXBgyFL7KqG3RoEGBws4CjRcXVbABDLZTgx -VITE_SOLANA_FEE_PAYER_ADDRESS=CgJhbUdHQNN5HBeNEN7J69Z89emh6BtyYX1CPEGwaeqi -VITE_CLAIMABLE_TOKEN_PROGRAM_ADDRESS=Ewkv3JahEFRKkcJmpoKB7pXbnUHwjAyXiwEo4ZY2rezQ +VITE_QUERY_PROPOSAL_START_BLOCK=11818009 +VITE_REGISTRY_ADDRESS=0xC611C82150b56E6e4Ec5973AcAbA8835Dd0d75A2 VITE_REWARDS_MANAGER_PROGRAM_ID=DDZDcYdQFEMwcu2Mwo75yGFjJ1mUQyyXLWzhZLEVFcei VITE_REWARDS_MANAGER_PROGRAM_PDA=71hWFVYokLaN1PNYzTAWi13EfJ7Xt9VbSWUKsXUT8mxE VITE_REWARDS_MANAGER_TOKEN_PDA=3V9opXNpHmPPymKeq7CYD8wWMH8wzFXmqEkNdzfsZhYq +VITE_SOLANA_CLUSTER_ENDPOINT=https://solana-mainnet.g.alchemy.com/v2/fCWqhO4QJ7X8XqXvi213Mzclmn-30-ie +VITE_SOLANA_FEE_PAYER_ADDRESS=CgJhbUdHQNN5HBeNEN7J69Z89emh6BtyYX1CPEGwaeqi +VITE_SOLANA_TOKEN_PROGRAM_ADDRESS=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA +VITE_SOLANA_WEB3_CLUSTER=mainnet-beta +VITE_USDC_MINT_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v +VITE_WAUDIO_MINT_ADDRESS=9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM +VITE_WORMHOLE_ADDRESS=0x6E7a1F7339bbB62b23D44797b63e4258d283E095 diff --git a/protocol-dashboard/.env.stage b/protocol-dashboard/.env.stage index 7f469e4df69..31999f457e7 100644 --- a/protocol-dashboard/.env.stage +++ b/protocol-dashboard/.env.stage @@ -1,30 +1,26 @@ -VITE_ENVIRONMENT=staging -VITE_ETH_TOKEN_ADDRESS=0x5375BE4c52fA29b26077B0F15ee5254D779676A6 -VITE_ETH_REGISTRY_ADDRESS=0xF27A9c44d7d5DDdA29bC1eeaD94718EeAC1775e3 -VITE_ETH_PROVIDER_URL=https://goerli.infura.io/v3/bcb203fe1f8d4eabb7798cf5de1d9097 -VITE_ETH_OWNER_WALLET= -VITE_QUERY_PROPOSAL_START_BLOCK = 1 - -VITE_ETH_NETWORK_ID=5 VITE_AUDIUS_NETWORK_ID=1056801 - VITE_AUDIUS_URL=https://staging.audius.co -VITE_GQL_URI=https://api.thegraph.com/subgraphs/name/audius-infra/audius-network-goerli -VITE_REGISTRY_ADDRESS=0x793373aBF96583d5eb71a15d86fFE732CD04D452 -VITE_ENTITY_MANAGER_ADDRESS=0x1Cd8a543596D499B9b6E7a6eC15ECd2B7857Fd64 - -VITE_IDENTITY_SERVICE_ENDPOINT=https://identityservice.staging.audius.co - -VITE_WORMHOLE_ADDRESS=0xf6f45e4d836da1d4ecd43bb1074620bfb0b7e0d7 -VITE_CLAIM_DISTRIBUTION_CONTRACT_ADDRESS=0x74b89B916c97d50557E8F944F32662fE52Ce378d -VITE_SOLANA_WEB3_CLUSTER=mainnet-beta -VITE_SOLANA_CLUSTER_ENDPOINT=https://solana-mainnet.g.alchemy.com/v2/N_o4w4Lgk2afO8uho9uuZu0LNi6gldVz -VITE_WAUDIO_MINT_ADDRESS=BELGiMZQ34SDE6x2FUaML2UHDAgBLS64xvhXjX5tBBZo -VITE_USDC_MINT_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -VITE_SOLANA_TOKEN_PROGRAM_ADDRESS=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA VITE_CLAIMABLE_TOKEN_PDA=Aw5AjygeMf9Nvg61BXvFSAzkqxcLqL8koepb14kvfc3W -VITE_SOLANA_FEE_PAYER_ADDRESS=E3CfijtAJwBSHfwFEViAUd3xp7c8TBxwC1eXn1Fgxp8h VITE_CLAIMABLE_TOKEN_PROGRAM_ADDRESS=2sjQNmUfkV6yKKi4dPR8gWRgtyma5aiymE3aXL2RAZww +VITE_CLAIM_DISTRIBUTION_CONTRACT_ADDRESS=0x74b89B916c97d50557E8F944F32662fE52Ce378d +VITE_ENTITY_MANAGER_ADDRESS=0x1Cd8a543596D499B9b6E7a6eC15ECd2B7857Fd64 +VITE_ENVIRONMENT=staging +VITE_ETH_NETWORK_ID=5 +VITE_ETH_OWNER_WALLET= +VITE_ETH_PROVIDER_URL=https://goerli.infura.io/v3/bcb203fe1f8d4eabb7798cf5de1d9097 +VITE_ETH_REGISTRY_ADDRESS=0xF27A9c44d7d5DDdA29bC1eeaD94718EeAC1775e3 +VITE_ETH_TOKEN_ADDRESS=0x5375BE4c52fA29b26077B0F15ee5254D779676A6 +VITE_GQL_URI=https://api.thegraph.com/subgraphs/name/audius-infra/audius-network-goerli +VITE_IDENTITY_SERVICE_ENDPOINT=https://identityservice.staging.audius.co +VITE_QUERY_PROPOSAL_START_BLOCK=1 +VITE_REGISTRY_ADDRESS=0x793373aBF96583d5eb71a15d86fFE732CD04D452 VITE_REWARDS_MANAGER_PROGRAM_ID=CDpzvz7DfgbF95jSSCHLX3ERkugyfgn9Fw8ypNZ1hfXp VITE_REWARDS_MANAGER_PROGRAM_PDA=GaiG9LDYHfZGqeNaoGRzFEnLiwUT7WiC6sA6FDJX9ZPq VITE_REWARDS_MANAGER_TOKEN_PDA=HJQj8P47BdA7ugjQEn45LaESYrxhiZDygmukt8iumFZJ +VITE_SOLANA_CLUSTER_ENDPOINT=https://solana-mainnet.g.alchemy.com/v2/N_o4w4Lgk2afO8uho9uuZu0LNi6gldVz +VITE_SOLANA_FEE_PAYER_ADDRESS=E3CfijtAJwBSHfwFEViAUd3xp7c8TBxwC1eXn1Fgxp8h +VITE_SOLANA_TOKEN_PROGRAM_ADDRESS=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA +VITE_SOLANA_WEB3_CLUSTER=mainnet-beta +VITE_USDC_MINT_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v +VITE_WAUDIO_MINT_ADDRESS=BELGiMZQ34SDE6x2FUaML2UHDAgBLS64xvhXjX5tBBZo +VITE_WORMHOLE_ADDRESS=0xf6f45e4d836da1d4ecd43bb1074620bfb0b7e0d7 diff --git a/protocol-dashboard/package.json b/protocol-dashboard/package.json index 14e218568a4..c0845b03fb4 100644 --- a/protocol-dashboard/package.json +++ b/protocol-dashboard/package.json @@ -68,6 +68,7 @@ "build:prod": "npm run configure-prod-env && ./node_modules/.bin/env-cmd -f .env.prod.local turbo run build", "lint": "npm run prettier:check", "lint:fix": "npm run prettier:write", + "lint:env": "dotenv-linter", "prettier:base": "prettier --parser typescript --single-quote --no-semi", "prettier:check": "npm run prettier:base -- --list-different \"src/**/*.{ts,tsx}\"", "prettier:write": "npm run prettier:base -- --write \"src/**/*.{ts,tsx}\"", @@ -78,7 +79,7 @@ "update-build:stage": "node ./scripts/updateBuild.cjs stage", "update-build:prod": "node ./scripts/updateBuild.cjs prod", "TODO - ADD TYPECHECK TO VERIFY: =========================": "", - "verify": "concurrently \"npm:lint:fix\"" + "verify": "concurrently \"npm:lint:fix\" \"npm:lint:env\"" }, "eslintConfig": { "extends": "react-app" @@ -96,6 +97,7 @@ ] }, "devDependencies": { + "@audius/dotenv-linter": "*", "@openzeppelin/test-helpers": "0.5.6", "@pinata/sdk": "1.1.13", "@tanstack/eslint-plugin-query": "5.0.5", diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh index 91966429658..0cf1d042120 100755 --- a/scripts/postinstall.sh +++ b/scripts/postinstall.sh @@ -17,6 +17,6 @@ if [[ -z "${SKIP_POD_INSTALL}" ]]; then cd ../../.. fi -# if [[ -z "${CI}" ]]; then -# ./dev-tools/setup.sh -# fi +if [[ -z "${CI}" ]]; then + ./dev-tools/setup.sh +fi diff --git a/turbo.json b/turbo.json index ccb340e3744..c69993cf5d1 100644 --- a/turbo.json +++ b/turbo.json @@ -6,6 +6,10 @@ "dependsOn": ["^build"], "outputMode": "new-only" }, + "rebuild": { + "cache": false, + "outputMode": "new-only" + }, "start": { "cache": false, "persistent": true, @@ -14,15 +18,15 @@ }, "lint": { "outputMode": "new-only", - "dependsOn": ["^build"] + "dependsOn": ["^build", "^rebuild"] }, "lint:fix": { "outputMode": "new-only", - "dependsOn": ["^build"] + "dependsOn": ["^build", "^rebuild"] }, "verify": { "outputMode": "new-only", - "dependsOn": ["^build"] + "dependsOn": ["^build", "^rebuild"] }, "test": { "outputs": ["coverage/**"], From 40583e677e2c1594bb4987dd422ab0f6c07d7512 Mon Sep 17 00:00:00 2001 From: Isaac Solo Date: Mon, 26 Feb 2024 18:52:27 -0800 Subject: [PATCH 6/6] Improve search analytics (#7696) --- .../collection-list/CollectionCard.tsx | 12 ++++-- .../collection-list/CollectionList.tsx | 6 ++- .../src/components/lineup-tile/TrackTile.tsx | 7 +++- .../src/components/lineup-tile/types.ts | 3 ++ .../mobile/src/components/lineup/Lineup.tsx | 20 ++++++---- .../mobile/src/components/lineup/types.ts | 7 ++++ .../components/profile-list/ProfileCard.tsx | 17 ++++++--- .../components/profile-list/ProfileList.tsx | 9 +++-- .../SuggestedArtistsList.tsx | 1 + .../search-results-screen/tabs/AlbumsTab.tsx | 6 +++ .../tabs/PlaylistsTab.tsx | 6 +++ .../tabs/ProfilesTab.tsx | 6 +++ .../search-results-screen/tabs/TracksTab.tsx | 16 +++++++- .../tabs/useFetchTabResultsEffect.tsx | 2 +- .../tabs/useTrackSearchResultSelect.tsx | 30 +++++++++++++++ .../SearchResults/SearchItem.tsx | 37 ++++++++++++++++--- .../src/components/lineup/LineupProvider.tsx | 13 +++++-- .../track/desktop/ConnectedTrackTile.tsx | 9 ++++- .../components/desktop/SearchPageContent.jsx | 30 ++++++++++++--- 19 files changed, 197 insertions(+), 40 deletions(-) create mode 100644 packages/mobile/src/screens/search-results-screen/tabs/useTrackSearchResultSelect.tsx diff --git a/packages/mobile/src/components/collection-list/CollectionCard.tsx b/packages/mobile/src/components/collection-list/CollectionCard.tsx index 57e78671cc9..d8148808d2f 100644 --- a/packages/mobile/src/components/collection-list/CollectionCard.tsx +++ b/packages/mobile/src/components/collection-list/CollectionCard.tsx @@ -30,6 +30,7 @@ const formatPlaylistCardSecondaryText = (saves: number, tracks: number) => { type FullCollectionCardProps = { collection: Collection style?: StyleProp + onPress?: (id: ID) => void /** Override for what number to show as the # of tracks. Optional. */ numTracks?: number } @@ -37,6 +38,7 @@ type FullCollectionCardProps = { type CollectionCardWithIdProps = { collectionId: ID style?: StyleProp + onPress?: (id: ID) => void } type CollectionCardProps = FullCollectionCardProps | CollectionCardWithIdProps @@ -44,12 +46,14 @@ type CollectionCardProps = FullCollectionCardProps | CollectionCardWithIdProps const FullCollectionCard = ({ collection, numTracks, - style + style, + onPress }: FullCollectionCardProps) => { const navigation = useNavigation() const handlePress = useCallback(() => { navigation.push('Collection', { id: collection.playlist_id }) - }, [navigation, collection]) + onPress?.(collection.playlist_id) + }, [navigation, collection.playlist_id, onPress]) const renderImage = useCallback( (props: ImageProps) => ( @@ -103,7 +107,8 @@ const useTrackCountWithOfflineOverride = (collection: Collection | null) => { const CollectionCardWithId = ({ collectionId, - style + style, + onPress }: CollectionCardWithIdProps) => { const collection = useSelector((state: CommonState) => getCollection(state, { id: collectionId }) @@ -115,6 +120,7 @@ const CollectionCardWithId = ({ collection={collection} numTracks={numTracks} style={style} + onPress={onPress} /> ) : null } diff --git a/packages/mobile/src/components/collection-list/CollectionList.tsx b/packages/mobile/src/components/collection-list/CollectionList.tsx index 79af4c5bf20..5ef99c5a1d8 100644 --- a/packages/mobile/src/components/collection-list/CollectionList.tsx +++ b/packages/mobile/src/components/collection-list/CollectionList.tsx @@ -31,6 +31,7 @@ type FullCollectionListProps = { /** Optional mapping of collection ids to the number that should be shown as the # of tracks in the collection's info card. Added this because im offline mode, the number of tracks downloaded may not yet match the actual number of tracks in the collection. */ collectionIdsToNumTracks?: Record renderItem?: ListRenderItem | null + onCollectionPress?: (id: ID) => void } & FullListProps & CreateCollectionTileProps @@ -50,6 +51,7 @@ const FullCollectionList = (props: FullCollectionListProps) => { createPlaylistTrackId, createPlaylistCallback, renderItem, + onCollectionPress, ...other } = props @@ -67,13 +69,15 @@ const FullCollectionList = (props: FullCollectionListProps) => { ), [ collectionIdsToNumTracks, createPlaylistCallback, createPlaylistSource, - createPlaylistTrackId + createPlaylistTrackId, + onCollectionPress ] ) diff --git a/packages/mobile/src/components/lineup-tile/TrackTile.tsx b/packages/mobile/src/components/lineup-tile/TrackTile.tsx index 1672f02a264..6bfc4b910d3 100644 --- a/packages/mobile/src/components/lineup-tile/TrackTile.tsx +++ b/packages/mobile/src/components/lineup-tile/TrackTile.tsx @@ -77,6 +77,7 @@ type TrackTileProps = LineupItemProps & { } export const TrackTileComponent = ({ + onPress, togglePlay, track, user, @@ -144,11 +145,13 @@ export const TrackTileComponent = ({ id: track_id, source: PlaybackSource.TRACK_TILE }) - }, [togglePlay, lineupTileProps.uid, track_id]) + onPress?.(track_id) + }, [togglePlay, lineupTileProps.uid, track_id, onPress]) const handlePressTitle = useCallback(() => { navigation.push('Track', { id: track_id }) - }, [navigation, track_id]) + onPress?.(track_id) + }, [navigation, onPress, track_id]) const playbackPositionInfo = useSelector((state) => getTrackPosition(state, { trackId: track_id, userId: currentUserId }) diff --git a/packages/mobile/src/components/lineup-tile/types.ts b/packages/mobile/src/components/lineup-tile/types.ts index cf91bf5feef..24a7608ca0d 100644 --- a/packages/mobile/src/components/lineup-tile/types.ts +++ b/packages/mobile/src/components/lineup-tile/types.ts @@ -42,6 +42,9 @@ export type LineupItemProps = { /** Function that will toggle play of a track */ togglePlay: (args: { uid: UID; id: ID; source: PlaybackSource }) => void + /** Function called when tile title or playback is pressed */ + onPress?: (id: ID) => void + /** Uid of the item */ uid: UID diff --git a/packages/mobile/src/components/lineup/Lineup.tsx b/packages/mobile/src/components/lineup/Lineup.tsx index 1b3f41b8003..33795890969 100644 --- a/packages/mobile/src/components/lineup/Lineup.tsx +++ b/packages/mobile/src/components/lineup/Lineup.tsx @@ -139,20 +139,21 @@ const LineupTileView = memo(function LineupTileView({ showLeadingElementArtistPick, leadingElementId, rankIconCount, - togglePlay + togglePlay, + onPress }: LineupTileViewProps) { - const LineupTile = getLineupTileComponent(item) - - if (LineupTile) { + const TrackOrCollectionTile = getLineupTileComponent(item) + if (TrackOrCollectionTile) { return ( - @@ -171,7 +172,8 @@ const LineupItemTile = memo(function LineupItemTile({ showLeadingElementArtistPick, leadingElementId, rankIconCount, - togglePlay + togglePlay, + onPress }: LineupItemTileProps) { if (!item) return null if ('_loading' in item) { @@ -188,6 +190,7 @@ const LineupItemTile = memo(function LineupItemTile({ leadingElementId={leadingElementId} rankIconCount={rankIconCount} togglePlay={togglePlay} + onPress={onPress} /> ) } @@ -227,6 +230,7 @@ export const Lineup = ({ extraFetchOptions, ListFooterComponent, EndOfLineupComponent, + onPressItem, ...listProps }: LineupProps) => { const dispatch = useDispatch() @@ -387,6 +391,7 @@ export const Lineup = ({ rankIconCount={rankIconCount} showLeadingElementArtistPick={showLeadingElementArtistPick} togglePlay={togglePlay} + onPress={onPressItem} /> ) }, @@ -395,7 +400,8 @@ export const Lineup = ({ leadingElementId, rankIconCount, showLeadingElementArtistPick, - togglePlay + togglePlay, + onPressItem ] ) diff --git a/packages/mobile/src/components/lineup/types.ts b/packages/mobile/src/components/lineup/types.ts index ca21e2aef43..ccecb782133 100644 --- a/packages/mobile/src/components/lineup/types.ts +++ b/packages/mobile/src/components/lineup/types.ts @@ -161,6 +161,12 @@ export type LineupProps = { * When `true`, add pull-to-refresh capability */ pullToRefresh?: boolean + + /** + * Function called when item is pressed + */ + onPressItem?: (id: ID) => void + EndOfLineupComponent?: ComponentType | ReactElement } & Pick< SectionListProps, @@ -181,6 +187,7 @@ export type LineupItemTileProps = Pick< item: LineupItem | LoadingLineupItem index: number togglePlay: ({ uid, id, source }: TogglePlayConfig) => void + onPress?: (id: ID) => void } export type LineupTileViewProps = Omit & { diff --git a/packages/mobile/src/components/profile-list/ProfileCard.tsx b/packages/mobile/src/components/profile-list/ProfileCard.tsx index 3bb8b4e2472..0a61c07062b 100644 --- a/packages/mobile/src/components/profile-list/ProfileCard.tsx +++ b/packages/mobile/src/components/profile-list/ProfileCard.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import type { User } from '@audius/common/models' +import type { ID, User } from '@audius/common/models' import { SquareSizes } from '@audius/common/models' import type { ImageProps } from '@audius/harmony-native' @@ -15,18 +15,23 @@ const formatProfileCardSecondaryText = (followers: number) => { return `${formatCount(followers)} ${followersText}` } -type ProfileCardProps = Partial & { +type ProfileCardProps = Partial> & { profile: User + preventNavigation?: boolean + onPress?: (id: ID) => void } export const ProfileCard = (props: ProfileCardProps) => { - const { profile, onPress, ...other } = props + const { profile, preventNavigation, onPress, ...other } = props const { user_id, handle } = profile const navigation = useNavigation() const handlePress = useCallback(() => { - navigation.push('Profile', { handle }) - }, [navigation, handle]) + if (!preventNavigation) { + navigation.push('Profile', { handle }) + } + onPress?.(user_id) + }, [navigation, handle, onPress, user_id, preventNavigation]) const renderImage = useCallback( (props: ImageProps) => ( @@ -44,7 +49,7 @@ export const ProfileCard = (props: ProfileCardProps) => { renderImage={renderImage} primaryText={profile.name} secondaryText={formatProfileCardSecondaryText(profile.follower_count)} - onPress={onPress ?? handlePress} + onPress={handlePress} type='user' user={profile} {...other} diff --git a/packages/mobile/src/components/profile-list/ProfileList.tsx b/packages/mobile/src/components/profile-list/ProfileList.tsx index 066f90311a5..2b9ab826ecf 100644 --- a/packages/mobile/src/components/profile-list/ProfileList.tsx +++ b/packages/mobile/src/components/profile-list/ProfileList.tsx @@ -1,4 +1,4 @@ -import type { User } from '@audius/common/models' +import type { ID, User } from '@audius/common/models' import type { CardListProps } from 'app/components/core' import { CardList } from 'app/components/core' @@ -10,14 +10,17 @@ type ListProps = Omit, 'data'> export type ProfileListProps = { profiles: User[] | undefined + onCardPress?: (user_id: ID) => void } & Partial export const ProfileList = (props: ProfileListProps) => { - const { profiles, ...other } = props + const { profiles, onCardPress, ...other } = props return ( } + renderItem={({ item }) => ( + + )} LoadingCardComponent={ProfileCardSkeleton} {...other} /> diff --git a/packages/mobile/src/components/suggested-follows/SuggestedArtistsList.tsx b/packages/mobile/src/components/suggested-follows/SuggestedArtistsList.tsx index 3adeb3b02e8..425bb1baf07 100644 --- a/packages/mobile/src/components/suggested-follows/SuggestedArtistsList.tsx +++ b/packages/mobile/src/components/suggested-follows/SuggestedArtistsList.tsx @@ -79,6 +79,7 @@ export const SuggestedArtistsList = (props: SuggestedArtistsListProps) => { return ( handleSelectArtist(user_id)} TileProps={{ as: LinearGradient, colors: gradientColors }} styles={textStyles} diff --git a/packages/mobile/src/screens/search-results-screen/tabs/AlbumsTab.tsx b/packages/mobile/src/screens/search-results-screen/tabs/AlbumsTab.tsx index caedc0d3225..708b9acb899 100644 --- a/packages/mobile/src/screens/search-results-screen/tabs/AlbumsTab.tsx +++ b/packages/mobile/src/screens/search-results-screen/tabs/AlbumsTab.tsx @@ -9,6 +9,7 @@ import { spacing } from 'app/styles/spacing' import { EmptyResults } from '../EmptyResults' import { useFetchTabResultsEffect } from './useFetchTabResultsEffect' +import { useTrackSearchResultSelect } from './useTrackSearchResultSelect' const { getSearchStatus } = searchResultsPageSelectors @@ -27,6 +28,10 @@ const selectSearchAlbums = (state: CommonState) => { } export const AlbumsTab = () => { + const onSelectSearchResult = useTrackSearchResultSelect( + 'album', + 'more results page' + ) const albums = useProxySelector(selectSearchAlbums, []) useFetchTabResultsEffect(SearchKind.ALBUMS) @@ -36,6 +41,7 @@ export const AlbumsTab = () => { isLoading={!albums} collection={albums} ListEmptyComponent={} + onCollectionPress={onSelectSearchResult} /> ) } diff --git a/packages/mobile/src/screens/search-results-screen/tabs/PlaylistsTab.tsx b/packages/mobile/src/screens/search-results-screen/tabs/PlaylistsTab.tsx index a4b3fb1e4f6..924116c8b57 100644 --- a/packages/mobile/src/screens/search-results-screen/tabs/PlaylistsTab.tsx +++ b/packages/mobile/src/screens/search-results-screen/tabs/PlaylistsTab.tsx @@ -9,6 +9,7 @@ import { spacing } from 'app/styles/spacing' import { EmptyResults } from '../EmptyResults' import { useFetchTabResultsEffect } from './useFetchTabResultsEffect' +import { useTrackSearchResultSelect } from './useTrackSearchResultSelect' const { getSearchStatus } = searchResultsPageSelectors @@ -27,6 +28,10 @@ const selectSearchPlaylists = (state: CommonState) => { } export const PlaylistsTab = () => { + const onSelectSearchResult = useTrackSearchResultSelect( + 'playlist', + 'more results page' + ) const playlists = useProxySelector(selectSearchPlaylists, []) useFetchTabResultsEffect(SearchKind.PLAYLISTS) @@ -36,6 +41,7 @@ export const PlaylistsTab = () => { isLoading={!playlists} collection={playlists} ListEmptyComponent={} + onCollectionPress={onSelectSearchResult} /> ) } diff --git a/packages/mobile/src/screens/search-results-screen/tabs/ProfilesTab.tsx b/packages/mobile/src/screens/search-results-screen/tabs/ProfilesTab.tsx index 5d1fe545b30..7da15e4a7f8 100644 --- a/packages/mobile/src/screens/search-results-screen/tabs/ProfilesTab.tsx +++ b/packages/mobile/src/screens/search-results-screen/tabs/ProfilesTab.tsx @@ -9,6 +9,7 @@ import { spacing } from 'app/styles/spacing' import { EmptyResults } from '../EmptyResults' import { useFetchTabResultsEffect } from './useFetchTabResultsEffect' +import { useTrackSearchResultSelect } from './useTrackSearchResultSelect' const { getSearchStatus } = searchResultsPageSelectors @@ -22,6 +23,10 @@ const selectSearchUsers = (state: CommonState) => { } export const ProfilesTab = () => { + const onSelectSearchResult = useTrackSearchResultSelect( + 'profile', + 'more results page' + ) const users = useProxySelector(selectSearchUsers, []) useFetchTabResultsEffect(SearchKind.USERS) @@ -29,6 +34,7 @@ export const ProfilesTab = () => { return ( } diff --git a/packages/mobile/src/screens/search-results-screen/tabs/TracksTab.tsx b/packages/mobile/src/screens/search-results-screen/tabs/TracksTab.tsx index 3d6c44624aa..4abed77542a 100644 --- a/packages/mobile/src/screens/search-results-screen/tabs/TracksTab.tsx +++ b/packages/mobile/src/screens/search-results-screen/tabs/TracksTab.tsx @@ -14,13 +14,20 @@ import { SearchQueryContext } from '../SearchQueryContext' import { SearchResultsTab } from './SearchResultsTab' import { useFetchTabResultsEffect } from './useFetchTabResultsEffect' +import { useTrackSearchResultSelect } from './useTrackSearchResultSelect' + const { getSearchTracksLineup } = searchResultsPageSelectors const { makeGetLineupMetadatas } = lineupSelectors const getSearchTracksLineupMetadatas = makeGetLineupMetadatas( getSearchTracksLineup ) -export const TracksTab = () => { +export const TracksTab = ({ route }) => { + const onSelectSearchResult = useTrackSearchResultSelect( + 'track', + 'more results page' + ) + const lineup = useSelector(getSearchTracksLineupMetadatas) const dispatch = useDispatch() const { query, isTagSearch } = useContext(SearchQueryContext) @@ -43,7 +50,12 @@ export const TracksTab = () => { noResults={lineup?.entries.length === 0} status={lineup?.status} > - + ) } diff --git a/packages/mobile/src/screens/search-results-screen/tabs/useFetchTabResultsEffect.tsx b/packages/mobile/src/screens/search-results-screen/tabs/useFetchTabResultsEffect.tsx index f49132c0a11..9cb20929686 100644 --- a/packages/mobile/src/screens/search-results-screen/tabs/useFetchTabResultsEffect.tsx +++ b/packages/mobile/src/screens/search-results-screen/tabs/useFetchTabResultsEffect.tsx @@ -77,7 +77,7 @@ export const useFetchTabResultsEffect = (searchKind: SearchKind) => { make({ eventName: EventNames.SEARCH_SEARCH, term: query, - source: 'search results page' + source: 'more results page' }) ) } diff --git a/packages/mobile/src/screens/search-results-screen/tabs/useTrackSearchResultSelect.tsx b/packages/mobile/src/screens/search-results-screen/tabs/useTrackSearchResultSelect.tsx new file mode 100644 index 00000000000..d99d73a824d --- /dev/null +++ b/packages/mobile/src/screens/search-results-screen/tabs/useTrackSearchResultSelect.tsx @@ -0,0 +1,30 @@ +import { useCallback } from 'react' + +import type { ID } from '@audius/common/models' +import { getSearchBarText } from 'audius-client/src/common/store/search-bar/selectors' +import { useSelector } from 'react-redux' + +import { make, track } from 'app/services/analytics' +import { EventNames } from 'app/types/analytics' + +export const useTrackSearchResultSelect = ( + kind: 'track' | 'profile' | 'playlist' | 'album', + source: 'autocomplete' | 'search results page' | 'more results page' +) => { + const searchQuery: string = useSelector(getSearchBarText) + const trackSearchResultSelect = useCallback( + (id: ID) => { + track( + make({ + eventName: EventNames.SEARCH_RESULT_SELECT, + term: searchQuery, + source, + kind, + id + }) + ) + }, + [searchQuery, kind, source] + ) // Dependencies array includes searchQuery and kind + return trackSearchResultSelect +} diff --git a/packages/mobile/src/screens/search-screen/SearchResults/SearchItem.tsx b/packages/mobile/src/screens/search-screen/SearchResults/SearchItem.tsx index 08a292c7913..288b0e7a8b5 100644 --- a/packages/mobile/src/screens/search-screen/SearchResults/SearchItem.tsx +++ b/packages/mobile/src/screens/search-screen/SearchResults/SearchItem.tsx @@ -14,6 +14,7 @@ import { CollectionImage } from 'app/components/image/CollectionImage' import { TrackImage } from 'app/components/image/TrackImage' import UserBadges from 'app/components/user-badges/UserBadges' import { useNavigation } from 'app/hooks/useNavigation' +import { useTrackSearchResultSelect } from 'app/screens/search-results-screen/tabs/useTrackSearchResultSelect' import { addItem } from 'app/store/search/searchSlice' import { makeStyles } from 'app/styles' @@ -49,11 +50,22 @@ const UserSearchResult = (props: UserSearchResultProps) => { const styles = useStyles() const navigation = useNavigation() const dispatch = useDispatch() - + const onSearchResultSelect = useTrackSearchResultSelect( + 'profile', + 'autocomplete' + ) const handlePress = useCallback(() => { dispatch(addItem({ searchItem: user.name })) navigation.push('Profile', { handle: user.handle }) - }, [user, navigation, dispatch]) + onSearchResultSelect(user.user_id) + }, [ + dispatch, + user.name, + user.handle, + user.user_id, + navigation, + onSearchResultSelect + ]) return ( @@ -75,6 +87,10 @@ const TrackSearchResult = (props: TrackSearchResultProps) => { const navigation = useNavigation() const dispatch = useDispatch() + const onSearchResultSelect = useTrackSearchResultSelect( + 'track', + 'autocomplete' + ) const handlePress = useCallback(() => { dispatch(addItem({ searchItem: track.title })) @@ -83,7 +99,8 @@ const TrackSearchResult = (props: TrackSearchResultProps) => { searchTrack: track, canBeUnlisted: false }) - }, [track, navigation, dispatch]) + onSearchResultSelect(track.track_id) + }, [dispatch, track, navigation, onSearchResultSelect]) return ( @@ -114,6 +131,10 @@ const PlaylistSearchResult = (props: PlaylistSearchResultProps) => { const navigation = useNavigation() const dispatch = useDispatch() + const onSearchResultSelect = useTrackSearchResultSelect( + 'playlist', + 'autocomplete' + ) const handlePress = useCallback(() => { dispatch(addItem({ searchItem: playlist.playlist_name })) @@ -121,7 +142,8 @@ const PlaylistSearchResult = (props: PlaylistSearchResultProps) => { id: playlist.playlist_id, searchCollection: playlist }) - }, [playlist, navigation, dispatch]) + onSearchResultSelect(playlist.playlist_id) + }, [dispatch, playlist, navigation, onSearchResultSelect]) return ( @@ -152,6 +174,10 @@ const AlbumSearchResult = (props: AlbumSearchResultProps) => { const navigation = useNavigation() const dispatch = useDispatch() + const onSearchResultSelect = useTrackSearchResultSelect( + 'album', + 'autocomplete' + ) const handlePress = useCallback(() => { dispatch(addItem({ searchItem: album.playlist_name })) @@ -159,7 +185,8 @@ const AlbumSearchResult = (props: AlbumSearchResultProps) => { id: album.playlist_id, searchCollection: album }) - }, [album, navigation, dispatch]) + onSearchResultSelect(album.playlist_id) + }, [dispatch, album, navigation, onSearchResultSelect]) return ( diff --git a/packages/web/src/components/lineup/LineupProvider.tsx b/packages/web/src/components/lineup/LineupProvider.tsx index ab041963ab8..3e131cd7f0f 100644 --- a/packages/web/src/components/lineup/LineupProvider.tsx +++ b/packages/web/src/components/lineup/LineupProvider.tsx @@ -129,7 +129,7 @@ export interface LineupProviderProps { playingUid: UID | null playingTrackId: ID | null playing: boolean - playTrack: (uid: UID) => void + playTrack: (uid: UID, trackId?: ID) => void pauseTrack: () => void variant: LineupVariant loadMore?: (offset: number, limit: number, overwrite: boolean) => void @@ -210,6 +210,9 @@ export interface LineupProviderProps { /** How many icons to show for top ranked entries in the lineup. Defaults to 0, showing none */ rankIconCount?: number + + /** Function triggered on click of tile */ + onClickTile?: (trackId: ID) => void } interface LineupProviderState { @@ -277,7 +280,7 @@ class LineupProvider extends PureComponent { togglePlay = (uid: UID, trackId: ID, source?: PlaybackSource) => { const { playTrack, pauseTrack, playing, playingUid, record } = this.props if (uid !== playingUid || (uid === playingUid && !playing)) { - playTrack(uid) + playTrack(uid, trackId) record( make(Name.PLAYBACK_PLAY, { id: `${trackId}`, @@ -487,7 +490,8 @@ class LineupProvider extends PureComponent { numPlaylistSkeletonRows, isTrending = false, showFeedTipTile = false, - rankIconCount = 0 + rankIconCount = 0, + onClickTile } = this.props const isMobile = this.context.isMobile const status = lineup.status @@ -537,7 +541,8 @@ class LineupProvider extends PureComponent { hasLoaded: this.hasLoaded, isTrending, showRankIcon: index < rankIconCount, - showFeedTipTile + showFeedTipTile, + onClick: onClickTile } if (entry.id === leadingElementId) { trackProps = { ...trackProps, ...leadingElementTileProps } diff --git a/packages/web/src/components/track/desktop/ConnectedTrackTile.tsx b/packages/web/src/components/track/desktop/ConnectedTrackTile.tsx index c35c9f786af..5bb99424921 100644 --- a/packages/web/src/components/track/desktop/ConnectedTrackTile.tsx +++ b/packages/web/src/components/track/desktop/ConnectedTrackTile.tsx @@ -81,6 +81,7 @@ type OwnProps = { isTrending: boolean showRankIcon: boolean isFeed: boolean + onClick?: (trackId: ID) => void } type ConnectedTrackTileProps = OwnProps & @@ -113,7 +114,8 @@ const ConnectedTrackTile = ({ shareTrack, isTrending, isFeed = false, - showRankIcon + showRankIcon, + onClick }: ConnectedTrackTileProps) => { const trackWithFallback = getTrackWithFallback(track) const { @@ -307,6 +309,10 @@ const ConnectedTrackTile = ({ shareTrack(trackId) }, [shareTrack, trackId]) + const onClickTitle = useCallback(() => { + onClick?.(trackId) + }, [onClick, trackId]) + const openLockedContentModal = useCallback(() => { dispatch(setLockedContentId({ id: trackId })) setLockedContentVisibility(true) @@ -394,6 +400,7 @@ const ConnectedTrackTile = ({ onClickShare={onClickShare} onClickLocked={openLockedContentModal} onTogglePlay={onTogglePlay} + onClickTitle={onClickTitle} isTrending={isTrending} showRankIcon={showRankIcon} permalink={permalink} diff --git a/packages/web/src/pages/search-page/components/desktop/SearchPageContent.jsx b/packages/web/src/pages/search-page/components/desktop/SearchPageContent.jsx index 61a15f91e73..e011c52de5e 100644 --- a/packages/web/src/pages/search-page/components/desktop/SearchPageContent.jsx +++ b/packages/web/src/pages/search-page/components/desktop/SearchPageContent.jsx @@ -1,6 +1,6 @@ import { Component } from 'react' -import { Status } from '@audius/common/models' +import { Status, Name } from '@audius/common/models' import { searchResultsPageTracksLineupActions as tracksActions, SearchKind @@ -9,6 +9,7 @@ import { formatCount } from '@audius/common/utils' import { IconSearch as IconBigSearch } from '@audius/harmony' import { Redirect } from 'react-router' +import { make } from 'common/store/analytics/actions' import Card from 'components/card/desktop/Card' import CategoryHeader from 'components/header/desktop/CategoryHeader' import Header from 'components/header/desktop/Header' @@ -308,7 +309,16 @@ class SearchPageContent extends Component { ) }) - + const onClickTile = (trackId, source) => { + this.props.dispatch( + make(Name.SEARCH_RESULT_SELECT, { + searchText, + kind: 'track', + id: trackId, + source + }) + ) + } const foundResults = artistCards.length > 0 || tracks.entries.length > 0 || @@ -354,9 +364,15 @@ class SearchPageContent extends Component { }) ) }} - playTrack={(uid) => this.props.dispatch(tracksActions.play(uid))} + playTrack={(uid, trackId) => { + onClickTile(trackId, 'more results page') + this.props.dispatch(tracksActions.play(uid)) + }} pauseTrack={() => this.props.dispatch(tracksActions.pause())} actions={tracksActions} + onClickTile={(trackId) => { + onClickTile(trackId, 'more results page') + }} />
@@ -441,11 +457,15 @@ class SearchPageContent extends Component { }) ) } - playTrack={(uid) => + playTrack={(uid, trackId) => { + onClickTile(trackId, 'search results page') this.props.dispatch(tracksActions.play(uid)) - } + }} pauseTrack={(uid) => this.props.dispatch(tracksActions.pause())} actions={tracksActions} + onClickTile={(trackId) => { + onClickTile(trackId, 'search results page') + }} /> ) : null}