diff --git a/packages/experiments/src/lib/idb/idb.ts b/packages/experiments/src/lib/idb/idb.ts index 779a5d52d..53aa01d96 100644 --- a/packages/experiments/src/lib/idb/idb.ts +++ b/packages/experiments/src/lib/idb/idb.ts @@ -3,7 +3,7 @@ import Log from '../utils/Log' export type IDBStores = 'up-down-watch-history' -const dbPromise = openDB('keyval-store', 8, { +const dbPromise = openDB('hot-or-not-experiments', 8, { upgrade(db) { if (!db.objectStoreNames.contains('up-down-watch-history')) { db.createObjectStore('up-down-watch-history') diff --git a/packages/hot-or-not-backend-canister b/packages/hot-or-not-backend-canister index 52364312a..617edad8e 160000 --- a/packages/hot-or-not-backend-canister +++ b/packages/hot-or-not-backend-canister @@ -1 +1 @@ -Subproject commit 52364312a6ef9d86e010798dc29706f1e24ef136 +Subproject commit 617edad8e4caa63d995dd062da26ad0e1a32d4de diff --git a/packages/web-client/declarations/configuration/configuration.did.d.ts b/packages/web-client/declarations/configuration/configuration.did.d.ts index d876caa9a..8072e55a8 100644 --- a/packages/web-client/declarations/configuration/configuration.did.d.ts +++ b/packages/web-client/declarations/configuration/configuration.did.d.ts @@ -4,9 +4,7 @@ import type { ActorMethod } from '@dfinity/agent'; export interface ConfigurationInitArgs { 'known_principal_ids' : [] | [Array<[KnownPrincipalType, Principal]>], 'signups_enabled' : [] | [boolean], - 'access_control_map' : [] | [Array<[Principal, Array]>], } -export type ErrorUpdateListOfWellKnownPrincipals = { 'Unauthorized' : null }; export type KnownPrincipalType = { 'CanisterIdUserIndex' : null } | { 'CanisterIdConfiguration' : null } | { 'CanisterIdProjectMemberIndex' : null } | @@ -17,30 +15,20 @@ export type KnownPrincipalType = { 'CanisterIdUserIndex' : null } | { 'CanisterIdSNSController' : null } | { 'UserIdGlobalSuperAdmin' : null }; export type Result = { 'Ok' : null } | - { 'Err' : ErrorUpdateListOfWellKnownPrincipals }; -export type UserAccessRole = { 'CanisterController' : null } | - { 'ProfileOwner' : null } | - { 'CanisterAdmin' : null } | - { 'ProjectCanister' : null }; + { 'Err' : string }; export interface _SERVICE { 'are_signups_enabled' : ActorMethod<[], boolean>, 'get_current_list_of_all_well_known_principal_values' : ActorMethod< [], Array<[KnownPrincipalType, Principal]> >, - 'get_user_roles' : ActorMethod<[Principal], Array>, 'get_well_known_principal_value' : ActorMethod< [KnownPrincipalType], [] | [Principal] >, - 'toggle_signups_enabled' : ActorMethod<[], undefined>, + 'toggle_signups_enabled' : ActorMethod<[], Result>, 'update_list_of_well_known_principals' : ActorMethod< [KnownPrincipalType, Principal], Result >, - 'update_user_add_role' : ActorMethod<[UserAccessRole, Principal], undefined>, - 'update_user_remove_role' : ActorMethod< - [UserAccessRole, Principal], - undefined - >, } diff --git a/packages/web-client/declarations/configuration/configuration.did.js b/packages/web-client/declarations/configuration/configuration.did.js index 1d1bc5bab..fc51290a5 100644 --- a/packages/web-client/declarations/configuration/configuration.did.js +++ b/packages/web-client/declarations/configuration/configuration.did.js @@ -10,28 +10,13 @@ export const idlFactory = ({ IDL }) => { 'CanisterIdSNSController' : IDL.Null, 'UserIdGlobalSuperAdmin' : IDL.Null, }); - const UserAccessRole = IDL.Variant({ - 'CanisterController' : IDL.Null, - 'ProfileOwner' : IDL.Null, - 'CanisterAdmin' : IDL.Null, - 'ProjectCanister' : IDL.Null, - }); const ConfigurationInitArgs = IDL.Record({ 'known_principal_ids' : IDL.Opt( IDL.Vec(IDL.Tuple(KnownPrincipalType, IDL.Principal)) ), 'signups_enabled' : IDL.Opt(IDL.Bool), - 'access_control_map' : IDL.Opt( - IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Vec(UserAccessRole))) - ), - }); - const ErrorUpdateListOfWellKnownPrincipals = IDL.Variant({ - 'Unauthorized' : IDL.Null, - }); - const Result = IDL.Variant({ - 'Ok' : IDL.Null, - 'Err' : ErrorUpdateListOfWellKnownPrincipals, }); + const Result = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : IDL.Text }); return IDL.Service({ 'are_signups_enabled' : IDL.Func([], [IDL.Bool], ['query']), 'get_current_list_of_all_well_known_principal_values' : IDL.Func( @@ -39,28 +24,17 @@ export const idlFactory = ({ IDL }) => { [IDL.Vec(IDL.Tuple(KnownPrincipalType, IDL.Principal))], ['query'], ), - 'get_user_roles' : IDL.Func( - [IDL.Principal], - [IDL.Vec(UserAccessRole)], - ['query'], - ), 'get_well_known_principal_value' : IDL.Func( [KnownPrincipalType], [IDL.Opt(IDL.Principal)], ['query'], ), - 'toggle_signups_enabled' : IDL.Func([], [], []), + 'toggle_signups_enabled' : IDL.Func([], [Result], []), 'update_list_of_well_known_principals' : IDL.Func( [KnownPrincipalType, IDL.Principal], [Result], [], ), - 'update_user_add_role' : IDL.Func([UserAccessRole, IDL.Principal], [], []), - 'update_user_remove_role' : IDL.Func( - [UserAccessRole, IDL.Principal], - [], - [], - ), }); }; export const init = ({ IDL }) => { @@ -75,20 +49,11 @@ export const init = ({ IDL }) => { 'CanisterIdSNSController' : IDL.Null, 'UserIdGlobalSuperAdmin' : IDL.Null, }); - const UserAccessRole = IDL.Variant({ - 'CanisterController' : IDL.Null, - 'ProfileOwner' : IDL.Null, - 'CanisterAdmin' : IDL.Null, - 'ProjectCanister' : IDL.Null, - }); const ConfigurationInitArgs = IDL.Record({ 'known_principal_ids' : IDL.Opt( IDL.Vec(IDL.Tuple(KnownPrincipalType, IDL.Principal)) ), 'signups_enabled' : IDL.Opt(IDL.Bool), - 'access_control_map' : IDL.Opt( - IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Vec(UserAccessRole))) - ), }); return [ConfigurationInitArgs]; }; diff --git a/packages/web-client/declarations/configuration/index.js b/packages/web-client/declarations/configuration/index.js index 815e67bfa..ee3c08f13 100644 --- a/packages/web-client/declarations/configuration/index.js +++ b/packages/web-client/declarations/configuration/index.js @@ -4,8 +4,14 @@ import { Actor, HttpAgent } from "@dfinity/agent"; import { idlFactory } from "./configuration.did.js"; export { idlFactory } from "./configuration.did.js"; -// CANISTER_ID is replaced by webpack based on node environment -export const canisterId = process.env.CONFIGURATION_CANISTER_ID; +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_CONFIGURATION || + process.env.CONFIGURATION_CANISTER_ID; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/packages/web-client/declarations/data_backup/index.js b/packages/web-client/declarations/data_backup/index.js index f55b0416b..f685e52fe 100644 --- a/packages/web-client/declarations/data_backup/index.js +++ b/packages/web-client/declarations/data_backup/index.js @@ -4,8 +4,14 @@ import { Actor, HttpAgent } from "@dfinity/agent"; import { idlFactory } from "./data_backup.did.js"; export { idlFactory } from "./data_backup.did.js"; -// CANISTER_ID is replaced by webpack based on node environment -export const canisterId = process.env.DATA_BACKUP_CANISTER_ID; +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_DATA_BACKUP || + process.env.DATA_BACKUP_CANISTER_ID; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/packages/web-client/declarations/individual_user_template/index.js b/packages/web-client/declarations/individual_user_template/index.js index 43f6dd77d..880a95a8a 100644 --- a/packages/web-client/declarations/individual_user_template/index.js +++ b/packages/web-client/declarations/individual_user_template/index.js @@ -4,8 +4,14 @@ import { Actor, HttpAgent } from "@dfinity/agent"; import { idlFactory } from "./individual_user_template.did.js"; export { idlFactory } from "./individual_user_template.did.js"; -// CANISTER_ID is replaced by webpack based on node environment -export const canisterId = process.env.INDIVIDUAL_USER_TEMPLATE_CANISTER_ID; +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_INDIVIDUAL_USER_TEMPLATE || + process.env.INDIVIDUAL_USER_TEMPLATE_CANISTER_ID; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts b/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts index bad714146..f59f33c71 100644 --- a/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts +++ b/packages/web-client/declarations/individual_user_template/individual_user_template.did.d.ts @@ -90,6 +90,7 @@ export type HotOrNotOutcomePayoutEvent = { }; export interface IndividualUserTemplateInitArgs { 'known_principal_ids' : [] | [Array<[KnownPrincipalType, Principal]>], + 'url_to_send_canister_metrics_to' : [] | [string], 'profile_owner' : [] | [Principal], 'upgrade_version_number' : [] | [bigint], } @@ -313,6 +314,7 @@ export interface _SERVICE { 'get_profile_details' : ActorMethod<[], UserProfileDetailsForFrontend>, 'get_rewarded_for_referral' : ActorMethod<[Principal, Principal], undefined>, 'get_rewarded_for_signing_up' : ActorMethod<[], undefined>, + 'get_user_caniser_cycle_balance' : ActorMethod<[], bigint>, 'get_user_utility_token_transaction_history_with_pagination' : ActorMethod< [bigint, bigint], Result_5 @@ -354,7 +356,10 @@ export interface _SERVICE { [Array], undefined >, - 'return_cycles_to_user_index_canister' : ActorMethod<[], undefined>, + 'return_cycles_to_user_index_canister' : ActorMethod< + [[] | [bigint]], + undefined + >, 'update_post_add_view_details' : ActorMethod< [bigint, PostViewDetailsFromFrontend], undefined diff --git a/packages/web-client/declarations/individual_user_template/individual_user_template.did.js b/packages/web-client/declarations/individual_user_template/individual_user_template.did.js index f3822b5e5..fd17122a8 100644 --- a/packages/web-client/declarations/individual_user_template/individual_user_template.did.js +++ b/packages/web-client/declarations/individual_user_template/individual_user_template.did.js @@ -14,6 +14,7 @@ export const idlFactory = ({ IDL }) => { 'known_principal_ids' : IDL.Opt( IDL.Vec(IDL.Tuple(KnownPrincipalType, IDL.Principal)) ), + 'url_to_send_canister_metrics_to' : IDL.Opt(IDL.Text), 'profile_owner' : IDL.Opt(IDL.Principal), 'upgrade_version_number' : IDL.Opt(IDL.Nat64), }); @@ -350,6 +351,7 @@ export const idlFactory = ({ IDL }) => { [], ), 'get_rewarded_for_signing_up' : IDL.Func([], [], []), + 'get_user_caniser_cycle_balance' : IDL.Func([], [IDL.Nat], ['query']), 'get_user_utility_token_transaction_history_with_pagination' : IDL.Func( [IDL.Nat64, IDL.Nat64], [Result_5], @@ -401,7 +403,11 @@ export const idlFactory = ({ IDL }) => { [], [], ), - 'return_cycles_to_user_index_canister' : IDL.Func([], [], []), + 'return_cycles_to_user_index_canister' : IDL.Func( + [IDL.Opt(IDL.Nat)], + [], + [], + ), 'update_post_add_view_details' : IDL.Func( [IDL.Nat64, PostViewDetailsFromFrontend], [], @@ -456,6 +462,7 @@ export const init = ({ IDL }) => { 'known_principal_ids' : IDL.Opt( IDL.Vec(IDL.Tuple(KnownPrincipalType, IDL.Principal)) ), + 'url_to_send_canister_metrics_to' : IDL.Opt(IDL.Text), 'profile_owner' : IDL.Opt(IDL.Principal), 'upgrade_version_number' : IDL.Opt(IDL.Nat64), }); diff --git a/packages/web-client/declarations/post_cache/index.js b/packages/web-client/declarations/post_cache/index.js index 18f47c17a..1c9ca59a2 100644 --- a/packages/web-client/declarations/post_cache/index.js +++ b/packages/web-client/declarations/post_cache/index.js @@ -4,8 +4,14 @@ import { Actor, HttpAgent } from "@dfinity/agent"; import { idlFactory } from "./post_cache.did.js"; export { idlFactory } from "./post_cache.did.js"; -// CANISTER_ID is replaced by webpack based on node environment -export const canisterId = process.env.POST_CACHE_CANISTER_ID; +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_POST_CACHE || + process.env.POST_CACHE_CANISTER_ID; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/packages/web-client/declarations/user_index/index.js b/packages/web-client/declarations/user_index/index.js index b3e98e69f..811244e1c 100644 --- a/packages/web-client/declarations/user_index/index.js +++ b/packages/web-client/declarations/user_index/index.js @@ -4,8 +4,14 @@ import { Actor, HttpAgent } from "@dfinity/agent"; import { idlFactory } from "./user_index.did.js"; export { idlFactory } from "./user_index.did.js"; -// CANISTER_ID is replaced by webpack based on node environment -export const canisterId = process.env.USER_INDEX_CANISTER_ID; +/* CANISTER_ID is replaced by webpack based on node environment + * Note: canister environment variable will be standardized as + * process.env.CANISTER_ID_ + * beginning in dfx 0.15.0 + */ +export const canisterId = + process.env.CANISTER_ID_USER_INDEX || + process.env.USER_INDEX_CANISTER_ID; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/packages/web-client/declarations/user_index/user_index.did.d.ts b/packages/web-client/declarations/user_index/user_index.did.d.ts index b326c86c4..5b1580721 100644 --- a/packages/web-client/declarations/user_index/user_index.did.d.ts +++ b/packages/web-client/declarations/user_index/user_index.did.d.ts @@ -4,23 +4,6 @@ import type { ActorMethod } from '@dfinity/agent'; export type CanisterInstallMode = { 'reinstall' : null } | { 'upgrade' : null } | { 'install' : null }; -export interface CanisterStatusResponse { - 'status' : CanisterStatusType, - 'memory_size' : bigint, - 'cycles' : bigint, - 'settings' : DefiniteCanisterSettings, - 'idle_cycles_burned_per_day' : bigint, - 'module_hash' : [] | [Uint8Array | number[]], -} -export type CanisterStatusType = { 'stopped' : null } | - { 'stopping' : null } | - { 'running' : null }; -export interface DefiniteCanisterSettings { - 'freezing_threshold' : bigint, - 'controllers' : Array, - 'memory_allocation' : bigint, - 'compute_allocation' : bigint, -} export type KnownPrincipalType = { 'CanisterIdUserIndex' : null } | { 'CanisterIdConfiguration' : null } | { 'CanisterIdProjectMemberIndex' : null } | @@ -30,9 +13,7 @@ export type KnownPrincipalType = { 'CanisterIdUserIndex' : null } | { 'CanisterIdPostCache' : null } | { 'CanisterIdSNSController' : null } | { 'UserIdGlobalSuperAdmin' : null }; -export type Result = { 'Ok' : CanisterStatusResponse } | - { 'Err' : string }; -export type Result_1 = { 'Ok' : null } | +export type Result = { 'Ok' : null } | { 'Err' : SetUniqueUsernameError }; export type SetUniqueUsernameError = { 'UsernameAlreadyTaken' : null } | { 'SendingCanisterDoesNotMatchUserCanisterId' : null } | @@ -44,7 +25,7 @@ export interface SystemTime { export interface UpgradeStatus { 'version_number' : bigint, 'last_run_on' : SystemTime, - 'failed_canister_ids' : Array<[Principal, Principal]>, + 'failed_canister_ids' : Array<[Principal, Principal, string]>, 'successful_upgrade_count' : number, } export type UserAccessRole = { 'CanisterController' : null } | @@ -57,10 +38,6 @@ export interface UserIndexInitArgs { } export interface _SERVICE { 'backup_all_individual_user_canisters' : ActorMethod<[], undefined>, - 'get_canister_status_from_management_canister' : ActorMethod< - [Principal], - Result - >, 'get_index_details_is_user_name_taken' : ActorMethod<[string], boolean>, 'get_index_details_last_upgrade_status' : ActorMethod<[], UpgradeStatus>, 'get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer' : ActorMethod< @@ -75,6 +52,8 @@ export interface _SERVICE { [Principal], [] | [Principal] >, + 'get_user_index_canister_count' : ActorMethod<[], bigint>, + 'get_user_index_canister_cycle_balance' : ActorMethod<[], bigint>, 'get_well_known_principal_value' : ActorMethod< [KnownPrincipalType], [] | [Principal] @@ -83,18 +62,9 @@ export interface _SERVICE { [Principal, Principal, string], undefined >, - 'retry_upgrade_for_canisters_that_failed_upgrade_with_the_latest_wasm' : ActorMethod< - [], - Array<[Principal, Principal, string]> - >, - 'topup_canisters_that_need_it' : ActorMethod<[], undefined>, 'update_index_with_unique_user_name_corresponding_to_user_principal_id' : ActorMethod< [string, Principal], - Result_1 - >, - 'update_user_index_upgrade_user_canisters_with_latest_wasm' : ActorMethod< - [], - string + Result >, 'upgrade_specific_individual_user_canister_with_latest_wasm' : ActorMethod< [Principal, Principal, [] | [CanisterInstallMode]], diff --git a/packages/web-client/declarations/user_index/user_index.did.js b/packages/web-client/declarations/user_index/user_index.did.js index e9619fa0a..f6fb4a034 100644 --- a/packages/web-client/declarations/user_index/user_index.did.js +++ b/packages/web-client/declarations/user_index/user_index.did.js @@ -24,29 +24,6 @@ export const idlFactory = ({ IDL }) => { IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Vec(UserAccessRole))) ), }); - const CanisterStatusType = IDL.Variant({ - 'stopped' : IDL.Null, - 'stopping' : IDL.Null, - 'running' : IDL.Null, - }); - const DefiniteCanisterSettings = IDL.Record({ - 'freezing_threshold' : IDL.Nat, - 'controllers' : IDL.Vec(IDL.Principal), - 'memory_allocation' : IDL.Nat, - 'compute_allocation' : IDL.Nat, - }); - const CanisterStatusResponse = IDL.Record({ - 'status' : CanisterStatusType, - 'memory_size' : IDL.Nat, - 'cycles' : IDL.Nat, - 'settings' : DefiniteCanisterSettings, - 'idle_cycles_burned_per_day' : IDL.Nat, - 'module_hash' : IDL.Opt(IDL.Vec(IDL.Nat8)), - }); - const Result = IDL.Variant({ - 'Ok' : CanisterStatusResponse, - 'Err' : IDL.Text, - }); const SystemTime = IDL.Record({ 'nanos_since_epoch' : IDL.Nat32, 'secs_since_epoch' : IDL.Nat64, @@ -54,7 +31,9 @@ export const idlFactory = ({ IDL }) => { const UpgradeStatus = IDL.Record({ 'version_number' : IDL.Nat64, 'last_run_on' : SystemTime, - 'failed_canister_ids' : IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Principal)), + 'failed_canister_ids' : IDL.Vec( + IDL.Tuple(IDL.Principal, IDL.Principal, IDL.Text) + ), 'successful_upgrade_count' : IDL.Nat32, }); const SetUniqueUsernameError = IDL.Variant({ @@ -62,7 +41,7 @@ export const idlFactory = ({ IDL }) => { 'SendingCanisterDoesNotMatchUserCanisterId' : IDL.Null, 'UserCanisterEntryDoesNotExist' : IDL.Null, }); - const Result_1 = IDL.Variant({ + const Result = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : SetUniqueUsernameError, }); @@ -73,11 +52,6 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'backup_all_individual_user_canisters' : IDL.Func([], [], []), - 'get_canister_status_from_management_canister' : IDL.Func( - [IDL.Principal], - [Result], - [], - ), 'get_index_details_is_user_name_taken' : IDL.Func( [IDL.Text], [IDL.Bool], @@ -103,6 +77,12 @@ export const idlFactory = ({ IDL }) => { [IDL.Opt(IDL.Principal)], ['query'], ), + 'get_user_index_canister_count' : IDL.Func([], [IDL.Nat64], ['query']), + 'get_user_index_canister_cycle_balance' : IDL.Func( + [], + [IDL.Nat], + ['query'], + ), 'get_well_known_principal_value' : IDL.Func( [KnownPrincipalType], [IDL.Opt(IDL.Principal)], @@ -113,20 +93,9 @@ export const idlFactory = ({ IDL }) => { [], [], ), - 'retry_upgrade_for_canisters_that_failed_upgrade_with_the_latest_wasm' : IDL.Func( - [], - [IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Principal, IDL.Text))], - [], - ), - 'topup_canisters_that_need_it' : IDL.Func([], [], []), 'update_index_with_unique_user_name_corresponding_to_user_principal_id' : IDL.Func( [IDL.Text, IDL.Principal], - [Result_1], - [], - ), - 'update_user_index_upgrade_user_canisters_with_latest_wasm' : IDL.Func( - [], - [IDL.Text], + [Result], [], ), 'upgrade_specific_individual_user_canister_with_latest_wasm' : IDL.Func( diff --git a/packages/web-client/src/components/icon/icon.type.ts b/packages/web-client/src/components/icon/icon.type.ts index 33c257818..cd696c149 100644 --- a/packages/web-client/src/components/icon/icon.type.ts +++ b/packages/web-client/src/components/icon/icon.type.ts @@ -78,3 +78,4 @@ export type IconName = | 'votes-graphics' | 'on-chain-dfinity-graphics' | 'stars-graphics' + | 'stamp' diff --git a/packages/web-client/src/components/layout/PlayerLayout.svelte b/packages/web-client/src/components/layout/PlayerLayout.svelte index ef3aba704..2446c1fd1 100644 --- a/packages/web-client/src/components/layout/PlayerLayout.svelte +++ b/packages/web-client/src/components/layout/PlayerLayout.svelte @@ -15,6 +15,7 @@ import { getShortNumber } from '$lib/utils/shortNumber' import { authState } from '$stores/auth' import userProfile from '$stores/userProfile' import { debounce } from 'throttle-debounce' +import ExperimentsPopup from '$components/popup/ExperimentsPopup.svelte' export let index: number export let post: PostPopulated @@ -25,6 +26,7 @@ export let showLikeButton = false export let showReportButton = false export let showHotOrNotButton = false export let showDescription = false +export let showExperimentsButton = false export let unavailable = false export let watchHistoryDb: 'watch' | 'watch-hon' export let source: 'hon_feed' | 'main_feed' | 'speculation' | 'post' @@ -35,6 +37,7 @@ let watchProgress = { partialWatchedPercentage: 0, } let showReportPopup = false +let showExperimentsPopup = false $: postPublisherId = post.created_by_unique_user_name[0] || post.created_by_user_principal_id @@ -205,6 +208,10 @@ $: avatarUrl = }} /> {/if} +{#if showExperimentsButton} + +{/if} + @@ -218,11 +225,28 @@ $: avatarUrl =
+ {#if showExperimentsButton} +
+ { + e.stopImmediatePropagation() + showExperimentsPopup = true + }}> +
+ NEW! +
+
+
+ {/if}
+ class="absolute z-[10] flex w-screen space-x-2 pl-4 pr-2 + {$$slots.hotOrNot ? 'bottom-40' : 'bottom-20'}">
+
+ class="max-w-16 pointer-events-auto flex shrink-0 flex-col items-end justify-end space-y-6 pb-2"> {#if showReportButton} +import Button from '$components/button/Button.svelte' +import IconButton from '$components/button/IconButton.svelte' +import { fade } from 'svelte/transition' + +export let show = false + + +{#if show} +
+
+ (show = false)} + class="absolute right-5 top-20 rounded-full bg-black/50 p-2" /> +
+
+ Try the Brand New Up + - + Down + Game +
+
+ Brought to you by Hot or Not +
+
+ + +
+
+{/if} diff --git a/packages/web-client/src/lib/helpers/feed.ts b/packages/web-client/src/lib/helpers/feed.ts index e060d0ee0..7c8d67410 100644 --- a/packages/web-client/src/lib/helpers/feed.ts +++ b/packages/web-client/src/lib/helpers/feed.ts @@ -7,6 +7,9 @@ import type { IDB } from '$lib/idb' import Log from '$lib/utils/Log' import { Principal } from '@dfinity/principal' import { individualUser, postCache } from './backend' +import { setBetDetailToDb } from './profile' +import sleep from '$lib/utils/sleep' +import { chunk } from '$lib/utils/chunk' export interface PostPopulated extends Omit, @@ -62,7 +65,7 @@ async function filterReportedPosts(posts: PostScoreIndexItem[]) { idb = (await import('$lib/idb')).idb } const keys = (await idb.keys('reported')) as string[] - if (!keys.length) return posts + if (!keys?.length) return posts const filtered = posts.filter( (o) => !keys.includes(o.publisher_canister_id.toText() + '@' + o.post_id), ) @@ -77,6 +80,28 @@ async function filterReportedPosts(posts: PostScoreIndexItem[]) { } } +async function filterStuckCanisterPosts(posts: PostScoreIndexItem[]) { + try { + const stuckCanisters = [ + '6l6jz-kaaaa-aaaao-actmq-cai', + 'rw62l-xyaaa-aaaao-aayca-cai', + '4thmb-taaaa-aaaao-aavfq-cai', + 'du74r-syaaa-aaaao-aa47q-cai', + 'u6qff-cqaaa-aaaao-aczfa-cai', + ] + return posts.filter( + (o) => !stuckCanisters.includes(o.publisher_canister_id.toText()), + ) + } catch (e) { + Log('error', 'Error while accessing IDB', { + error: e, + from: 'feed.filterReportedPosts', + type: 'idb', + }) + return posts + } +} + export async function getWatchedVideosFromCache( dbStore: 'watch' | 'watch-hon', ): Promise { @@ -112,10 +137,11 @@ export async function getTopPosts( BigInt(from + numberOfPosts), ) if ('Ok' in res) { - const filteredReportedPosts = await filterReportedPosts(res.Ok) - const filteredPosts = await filterPosts(filteredReportedPosts, 'watch') + const nonReportedPosts = await filterReportedPosts(res.Ok) + const notStuckPosts = await filterStuckCanisterPosts(nonReportedPosts) + const notWatchedPosts = await filterPosts(notStuckPosts, 'watch') const populatedRes = await populatePosts( - filterViewed ? filteredPosts : filteredReportedPosts, + filterViewed ? notWatchedPosts : notStuckPosts, ) if (populatedRes.error) { throw new Error( @@ -183,12 +209,11 @@ export async function getHotOrNotPosts( BigInt(from + numberOfPosts), ) if ('Ok' in res) { - const filteredNonBetPosts = await filterBets(res.Ok) - const filteredReportedPosts = await filterReportedPosts( - filteredNonBetPosts, - ) - // const filteredPosts = await filterPosts(filteredNonBetPosts, 'watch-hon') - const populatedRes = await populatePosts(filteredReportedPosts, true) + const notBetPosts = await filterBets(res.Ok) + const notStuckPosts = await filterStuckCanisterPosts(notBetPosts) + const notReportedPosts = await filterReportedPosts(notStuckPosts) + // const notWatchedPosts = await filterPosts(notReportedPosts, 'watch-hon') + const populatedRes = await populatePosts(notReportedPosts, true) if (populatedRes.error) { throw new Error( `Error while populating, ${JSON.stringify(populatedRes)}`, @@ -250,6 +275,43 @@ function hasUserBetOnPost(post: PostDetailsForFrontend) { return false } +async function fetchPostDetailById( + post: PostScoreIndexItem, + filterBetPosts = false, +) { + try { + const r = await individualUser( + Principal.from(post.publisher_canister_id), + ).get_individual_post_details_by_id(post.post_id) + if (filterBetPosts && (hasUserBetOnPost(r) || isBettingClosed(r))) { + Log('warn', 'Already bet on post', { + post, + from: 'feed.populatePosts.fetch', + }) + setBetDetailToDb({ + ...r, + ...post, + created_by_user_principal_id: r.created_by_user_principal_id.toText(), + publisher_canister_id: post.publisher_canister_id.toText(), + } satisfies PostPopulated) + return undefined + } + return { + ...r, + ...post, + created_by_user_principal_id: r.created_by_user_principal_id.toText(), + publisher_canister_id: post.publisher_canister_id.toText(), + } as PostPopulated + } catch (e) { + Log('error', 'Error while populating post', { + error: e, + post, + from: 'feed.populatePosts.fetch', + }) + return undefined + } +} + async function populatePosts( posts: PostScoreIndexItem[], filterBetPosts = false, @@ -259,28 +321,24 @@ async function populatePosts( return { posts: [], error: false } } - const res = await Promise.all( - posts.map(async (post) => { - try { - const r = await individualUser( - Principal.from(post.publisher_canister_id), - ).get_individual_post_details_by_id(post.post_id) - if (filterBetPosts && (hasUserBetOnPost(r) || isBettingClosed(r))) { - return undefined - } - return { - ...r, - ...post, - created_by_user_principal_id: - r.created_by_user_principal_id.toText(), - publisher_canister_id: post.publisher_canister_id.toText(), - } - } catch (_) { - return undefined - } - }), - ) - return { posts: res.filter((o) => !!o) as PostPopulated[], error: false } + const populatedPosts: PostPopulated[] = [] + + const chunkedPosts = chunk(posts, 10) + while (chunkedPosts.length) { + const batch = chunkedPosts.shift() + if (batch) { + const results = await Promise.all( + batch.map((post) => fetchPostDetailById(post, filterBetPosts)), + ) + populatedPosts.push(...(results.filter((o) => !!o) as PostPopulated[])) + await sleep(200) + } + } + + return { + posts: populatedPosts, + error: false, + } } catch (e) { Log('error', 'Error while loading posts', { error: e, diff --git a/packages/web-client/src/lib/helpers/profile.ts b/packages/web-client/src/lib/helpers/profile.ts index fa832e5df..b6f2e60c3 100644 --- a/packages/web-client/src/lib/helpers/profile.ts +++ b/packages/web-client/src/lib/helpers/profile.ts @@ -494,7 +494,7 @@ async function transformHistoryRecords( export async function setBetDetailToDb( post: PostPopulated, - betDetail: PlacedBetDetail, + betDetail?: PlacedBetDetail, ) { if (!post) return diff --git a/packages/web-client/src/lib/idb/db.ts b/packages/web-client/src/lib/idb/db.ts index 3f3fc7655..e42754544 100644 --- a/packages/web-client/src/lib/idb/db.ts +++ b/packages/web-client/src/lib/idb/db.ts @@ -9,7 +9,7 @@ type DBStores = | 'wallet' | 'reported' -const dbPromise = openDB('keyval-store', 7, { +const dbPromise = openDB('hot-or-not-web-client', 8, { upgrade(db) { if (!db.objectStoreNames.contains('keyval')) { db.createObjectStore('keyval') diff --git a/packages/web-client/src/lib/utils/chunk.ts b/packages/web-client/src/lib/utils/chunk.ts new file mode 100644 index 000000000..422e4730a --- /dev/null +++ b/packages/web-client/src/lib/utils/chunk.ts @@ -0,0 +1,4 @@ +export const chunk = (arr: T[], size: number): T[][] => + [...Array(Math.ceil(arr.length / size))].map((_, i) => + arr.slice(size * i, size + size * i), + ) diff --git a/packages/web-client/src/routes/(feed)/(splash)/hotornot/[id=videoId]/+page.svelte b/packages/web-client/src/routes/(feed)/(splash)/hotornot/[id=videoId]/+page.svelte index f30841800..d43255693 100644 --- a/packages/web-client/src/routes/(feed)/(splash)/hotornot/[id=videoId]/+page.svelte +++ b/packages/web-client/src/routes/(feed)/(splash)/hotornot/[id=videoId]/+page.svelte @@ -33,7 +33,7 @@ let videos: PostPopulated[] = [] let currentVideoIndex = 0 let lastWatchedVideoIndex = -1 let noMoreVideos = false -let loading = false +let loading = true let fetchedVideosCount = 0 let loadTimeout: ReturnType | undefined = undefined @@ -66,7 +66,6 @@ async function fetchNextVideos(force = false) { } else { clearTimeout(loadTimeout) showError = true - loading = false } return } else { @@ -87,10 +86,11 @@ async function fetchNextVideos(force = false) { } await tick() - loading = false Log('info', 'Fetched videos for feed', { noMoreVideos, + from: res.from, + fetchedCount: res.posts.length, source: 'hotOrNot.fetchNextVideos', }) } catch (e) { @@ -99,6 +99,7 @@ async function fetchNextVideos(force = false) { noMoreVideos, source: 'hotOrNot.fetchNextVideos', }) + } finally { loading = false } } @@ -165,6 +166,7 @@ beforeNavigate(() => { watchHistoryDb="watch-hon" showWalletLink showReportButton + showExperimentsButton let:recordView let:updateStats> + + + \ No newline at end of file diff --git a/packages/web-client/tailwind.config.cjs b/packages/web-client/tailwind.config.cjs index e721903d6..4dfe7a406 100644 --- a/packages/web-client/tailwind.config.cjs +++ b/packages/web-client/tailwind.config.cjs @@ -18,6 +18,7 @@ const config = { }, animation: { 'spin-slow': 'spin 3s linear infinite', + 'spin-slower': 'spin 8s linear infinite', }, }, },