-
+
{#if nft.downloadMetadata?.error || nft.downloadMetadata?.warning}
@@ -79,8 +96,8 @@
transition-property: background-color, border-color, box-shadow;
}
- &:hover,
- &:focus {
+ &:hover :not(.disabled),
+ &:focus :not(.disabled) {
container {
@apply shadow-lg dark:shadow-violet-900/25;
@apply border-2 border-brand-500;
diff --git a/packages/shared/src/lib/contexts/campaigns/constants/campaign-end-date-cut-off.constant.ts b/packages/shared/src/lib/contexts/campaigns/constants/campaign-end-date-cut-off.constant.ts
new file mode 100644
index 0000000000..347b30383d
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/constants/campaign-end-date-cut-off.constant.ts
@@ -0,0 +1 @@
+export const CAMPAIGN_END_DATE_CUT_OFF = '2024-01-31'
diff --git a/packages/shared/src/lib/contexts/campaigns/constants/campaign-poll-interval.constant.ts b/packages/shared/src/lib/contexts/campaigns/constants/campaign-poll-interval.constant.ts
new file mode 100644
index 0000000000..0680185e73
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/constants/campaign-poll-interval.constant.ts
@@ -0,0 +1,5 @@
+import { MILLISECONDS_PER_SECOND } from '@core/utils'
+
+export const INTERVAL_IN_SECONDS = 10
+
+export const CAMPAIGN_POLL_INTERVAL = INTERVAL_IN_SECONDS * MILLISECONDS_PER_SECOND
diff --git a/packages/shared/src/lib/contexts/campaigns/constants/featured-campaigns.constant.ts b/packages/shared/src/lib/contexts/campaigns/constants/featured-campaigns.constant.ts
new file mode 100644
index 0000000000..9771fd40e9
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/constants/featured-campaigns.constant.ts
@@ -0,0 +1,5 @@
+export const featuredCampaigns: string[] = [
+ '58ab133e-c54c-481a-aedf-e552aef7b6f5',
+ '3d45bd99-0537-45bc-9f00-4f21a4141ff9',
+ '450f4fab-419c-400a-87e3-41f8202dbbeb',
+]
diff --git a/packages/shared/src/lib/contexts/campaigns/constants/index.ts b/packages/shared/src/lib/contexts/campaigns/constants/index.ts
new file mode 100644
index 0000000000..f514b8acfa
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/constants/index.ts
@@ -0,0 +1,3 @@
+export * from './campaign-poll-interval.constant'
+export * from './campaign-end-date-cut-off.constant'
+export * from './featured-campaigns.constant'
diff --git a/packages/shared/src/lib/contexts/campaigns/enums/campaign-status.enum.ts b/packages/shared/src/lib/contexts/campaigns/enums/campaign-status.enum.ts
new file mode 100644
index 0000000000..28ec777103
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/enums/campaign-status.enum.ts
@@ -0,0 +1,6 @@
+export enum CampaignStatus {
+ Upcoming = 'upcoming',
+ Active = 'active',
+ Ended = 'ended',
+ Draft = 'draft',
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/enums/index.ts b/packages/shared/src/lib/contexts/campaigns/enums/index.ts
new file mode 100644
index 0000000000..7f250ec198
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/enums/index.ts
@@ -0,0 +1 @@
+export * from './campaign-status.enum'
diff --git a/packages/shared/src/lib/contexts/campaigns/index.ts b/packages/shared/src/lib/contexts/campaigns/index.ts
new file mode 100644
index 0000000000..fae3c61f89
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/index.ts
@@ -0,0 +1,5 @@
+export * from './constants'
+export * from './enums'
+export * from './interfaces'
+export * from './stores' // TODO: do not export stores at this level
+export * from './utils'
diff --git a/packages/shared/src/lib/contexts/campaigns/interfaces/campaign.interface.ts b/packages/shared/src/lib/contexts/campaigns/interfaces/campaign.interface.ts
new file mode 100644
index 0000000000..6f329db4d7
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/interfaces/campaign.interface.ts
@@ -0,0 +1,17 @@
+import { TideListingStatus } from '@core/tide'
+
+export interface ICampaign {
+ id: string
+ projectId: number
+ title: string
+ description: string
+ address: string
+ imageUrl: string
+ participants: number
+ startTime: string
+ endTime: string
+ url: string
+ chainId: number
+ listingStatus: TideListingStatus
+ ERC20Reward: unknown
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/interfaces/index.ts b/packages/shared/src/lib/contexts/campaigns/interfaces/index.ts
new file mode 100644
index 0000000000..f674575613
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/interfaces/index.ts
@@ -0,0 +1 @@
+export * from './campaign.interface'
diff --git a/packages/shared/src/lib/contexts/campaigns/stores/campaign-leaderboards.store.ts b/packages/shared/src/lib/contexts/campaigns/stores/campaign-leaderboards.store.ts
new file mode 100644
index 0000000000..b097516be5
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/stores/campaign-leaderboards.store.ts
@@ -0,0 +1,45 @@
+import { writable } from 'svelte/store'
+import { ITideLeaderboardItem } from '@core/tide/interfaces'
+
+export const campaignLeaderboards = writable<{
+ [projectId: number]: {
+ [campaignId: string]: {
+ board: ITideLeaderboardItem[]
+ userPosition: ITideLeaderboardItem | undefined
+ }
+ }
+}>({})
+
+export function addCampaignLeaderboard(
+ projectId: number,
+ campaignId: string,
+ leaderboard: ITideLeaderboardItem[]
+): void {
+ campaignLeaderboards.update((state) => {
+ if (!state[projectId]) {
+ state[projectId] = {}
+ }
+ if (!state[projectId][campaignId]) {
+ state[projectId][campaignId] = { board: [], userPosition: undefined }
+ }
+ state[projectId][campaignId].board = leaderboard
+ return state
+ })
+}
+
+export function addUserPositionToCampaignLeaderboard(
+ projectId: number,
+ campaignId: string,
+ userPosition: ITideLeaderboardItem
+): void {
+ campaignLeaderboards.update((state) => {
+ if (!state[projectId]) {
+ state[projectId] = {}
+ }
+ if (!state[projectId][campaignId]) {
+ state[projectId][campaignId] = { board: [], userPosition: undefined }
+ }
+ state[projectId][campaignId].userPosition = userPosition
+ return state
+ })
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/stores/campaigns-per-chain.store.ts b/packages/shared/src/lib/contexts/campaigns/stores/campaigns-per-chain.store.ts
new file mode 100644
index 0000000000..3fded9f068
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/stores/campaigns-per-chain.store.ts
@@ -0,0 +1,26 @@
+import { get, writable } from 'svelte/store'
+import { ICampaign } from '../interfaces'
+
+export const campaignsPerChain = writable<{ [chainId: number]: { [campaignId: string]: ICampaign } }>({})
+
+export function addCampaignForChain(chainId: number, campaigns: ICampaign[]): void {
+ campaignsPerChain.update((state) => {
+ if (!state[chainId]) {
+ state[chainId] = {}
+ }
+ campaigns.forEach((campaign) => {
+ state[chainId][campaign.id] = campaign
+ })
+ return state
+ })
+}
+
+export function getCampaignsForChains(chainIds: number[]): ICampaign[] {
+ const _chainsPerCampaign = get(campaignsPerChain)
+ let campaigns: ICampaign[] = []
+ for (const chainId of chainIds) {
+ const chainCampaigns = Object.values(_chainsPerCampaign[chainId] ?? {})
+ campaigns = [...campaigns, ...chainCampaigns]
+ }
+ return campaigns
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/stores/index.ts b/packages/shared/src/lib/contexts/campaigns/stores/index.ts
new file mode 100644
index 0000000000..a2fe7b01bb
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/stores/index.ts
@@ -0,0 +1,3 @@
+export * from './campaign-leaderboards.store'
+export * from './/campaigns-per-chain.store'
+export * from './selectedCampaign.store'
diff --git a/packages/shared/src/lib/contexts/campaigns/stores/selectedCampaign.store.ts b/packages/shared/src/lib/contexts/campaigns/stores/selectedCampaign.store.ts
new file mode 100644
index 0000000000..a708251564
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/stores/selectedCampaign.store.ts
@@ -0,0 +1,8 @@
+import { writable } from 'svelte/store'
+import { ICampaign } from '../interfaces'
+
+export const selectedCampaign = writable(undefined)
+
+export function setSelectedCampaign(campaign: ICampaign): void {
+ selectedCampaign.set(campaign)
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/utils/calculateCampaignStatus.ts b/packages/shared/src/lib/contexts/campaigns/utils/calculateCampaignStatus.ts
new file mode 100644
index 0000000000..b07c0d0b36
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/utils/calculateCampaignStatus.ts
@@ -0,0 +1,20 @@
+import { TideListingStatus } from '@core/tide'
+import { CampaignStatus } from '../enums'
+import { ICampaign } from '../interfaces'
+
+export function calculateCampaignStatus(campaign: ICampaign, now: Date): CampaignStatus {
+ const startDate = new Date(campaign.startTime)
+ const endDate = new Date(campaign.endTime)
+
+ if (campaign.listingStatus === TideListingStatus.Listed) {
+ if (now < startDate) {
+ return CampaignStatus.Upcoming
+ } else if (now > endDate) {
+ return CampaignStatus.Ended
+ } else {
+ return CampaignStatus.Active
+ }
+ } else {
+ return CampaignStatus.Draft
+ }
+}
diff --git a/packages/shared/src/lib/contexts/campaigns/utils/index.ts b/packages/shared/src/lib/contexts/campaigns/utils/index.ts
new file mode 100644
index 0000000000..ed503d231e
--- /dev/null
+++ b/packages/shared/src/lib/contexts/campaigns/utils/index.ts
@@ -0,0 +1 @@
+export * from './calculateCampaignStatus'
diff --git a/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts b/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts
index cc5216b967..83a3f6c0f2 100644
--- a/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts
+++ b/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts
@@ -32,4 +32,5 @@ export const externalAllowedLinks = [
// Other
'support.ledger.com',
'medium.com',
+ 'tideprotocol.xyz',
]
diff --git a/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts b/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
index 6c4da64722..8efa03a336 100644
--- a/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
+++ b/packages/shared/src/lib/core/layer-2/actions/fetchL2BalanceForAccount.ts
@@ -39,7 +39,7 @@ export function fetchL2BalanceForAccount(account: IAccountState): void {
await fetchLayer2Nfts(evmAddress, chain, account)
if (features.collectibles.erc721.enabled) {
- updateErc721NftsOwnership(account)
+ void updateErc721NftsOwnership(account)
}
const balances = await getLayer2BalanceForAddress(evmAddress, chain)
diff --git a/packages/shared/src/lib/core/market/apis/coingecko.api.ts b/packages/shared/src/lib/core/market/apis/coingecko.api.ts
index 29afcfb462..0541c5ac5b 100644
--- a/packages/shared/src/lib/core/market/apis/coingecko.api.ts
+++ b/packages/shared/src/lib/core/market/apis/coingecko.api.ts
@@ -1,4 +1,4 @@
-import { DEFAULT_APPLICATION_JSON_REQUEST_OPTIONS } from '@core/utils'
+import { DEFAULT_APPLICATION_JSON_REQUEST_OPTIONS, buildQueryParametersFromObject } from '@core/utils'
import { MARKET_API_BASE_URL } from '../constants'
import { CoinGeckoApiEndpoint, CoinGeckoNetworkId, MarketCoinId, MarketCurrency } from '../enums'
import { MarketCoinPrices } from '../types'
@@ -46,9 +46,3 @@ export class CoinGeckoApi {
return this.makeRequest(`${CoinGeckoApiEndpoint.COIN_DETAILS}/${id}`)
}
}
-
-function buildQueryParametersFromObject(obj: Record): string {
- return Object.keys(obj)
- .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
- .join('&')
-}
diff --git a/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts b/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
index 96bfa3bb3f..456249f485 100644
--- a/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
+++ b/packages/shared/src/lib/core/nfts/actions/updateErc721NftOwnership.ts
@@ -8,26 +8,29 @@ import { get } from 'svelte/store'
import { calculateAndAddPersistedNftBalanceChange } from '@core/activity'
export async function updateErc721NftsOwnership(account: IAccountState): Promise {
- const trackedErc721Nfts =
- (getAllAccountNfts()[account.index]?.filter((nft) => {
- return nft.standard === NftStandard.Erc721
- }) as IErc721Nft[]) ?? []
+ try {
+ const trackedErc721Nfts =
+ (getAllAccountNfts()[account.index]?.filter((nft) => {
+ return nft.standard === NftStandard.Erc721
+ }) as IErc721Nft[]) ?? []
+ const promises = trackedErc721Nfts.map(async (nft) => {
+ const updatedOwner = await getOwnerOfErc721Nft(nft)
+ const persistedNft = get(persistedNftForActiveProfile)?.[nft.id]
+ if (!persistedNft) {
+ return
+ }
- const promises = trackedErc721Nfts.map(async (nft) => {
- const updatedOwner = await getOwnerOfErc721Nft(nft)
- const persistedNft = get(persistedNftForActiveProfile)?.[nft.id]
- if (!persistedNft) {
- return
- }
+ if (persistedNft.ownerAddress !== updatedOwner) {
+ updatePersistedNft(nft.id, { ownerAddress: updatedOwner })
+ }
+ const l2Address = getAddressFromAccountForNetwork(account, nft.networkId)
+ const isSpendable = updatedOwner === l2Address?.toLowerCase()
+ updateAllAccountNftsForAccount(account.index, { ...nft, isSpendable })
- if (persistedNft.ownerAddress !== updatedOwner) {
- updatePersistedNft(nft.id, { ownerAddress: updatedOwner })
- }
- const l2Address = getAddressFromAccountForNetwork(account, nft.networkId)
- const isSpendable = updatedOwner === l2Address?.toLowerCase()
- updateAllAccountNftsForAccount(account.index, { ...nft, isSpendable })
-
- calculateAndAddPersistedNftBalanceChange(account, nft.networkId, nft.id, isSpendable)
- })
- await Promise.all(promises)
+ calculateAndAddPersistedNftBalanceChange(account, nft.networkId, nft.id, isSpendable)
+ })
+ await Promise.allSettled(promises)
+ } catch (error) {
+ console.error(error)
+ }
}
diff --git a/packages/shared/src/lib/core/router/enums/dashboard-route.enum.ts b/packages/shared/src/lib/core/router/enums/dashboard-route.enum.ts
index bd53606f5b..41e93deb57 100644
--- a/packages/shared/src/lib/core/router/enums/dashboard-route.enum.ts
+++ b/packages/shared/src/lib/core/router/enums/dashboard-route.enum.ts
@@ -1,8 +1,9 @@
export enum DashboardRoute {
- Wallet = 'wallet',
+ BuySell = 'buySell',
+ Campaigns = 'campaigns',
Collectibles = 'collectibles',
Developer = 'developer',
Governance = 'governance',
- BuySell = 'buySell',
Settings = 'settings',
+ Wallet = 'wallet',
}
diff --git a/packages/shared/src/lib/core/tide/apis/index.ts b/packages/shared/src/lib/core/tide/apis/index.ts
new file mode 100644
index 0000000000..d8e5ff47d2
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/apis/index.ts
@@ -0,0 +1 @@
+export * from './tide.api'
diff --git a/packages/shared/src/lib/core/tide/apis/tide.api.ts b/packages/shared/src/lib/core/tide/apis/tide.api.ts
new file mode 100644
index 0000000000..780a88bee4
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/apis/tide.api.ts
@@ -0,0 +1,113 @@
+import { INftAttribute } from '@core/nfts'
+import { BaseApi, buildQueryParametersFromObject } from '@core/utils'
+import { TIDE_API_BASE_URL } from '../constants'
+import { TideApiEndpoint } from '../enums'
+import { ITideLeaderboardItem, ITideUserPosition } from '../interfaces'
+
+type ProjectLeaderboardQueryParams = {
+ nextPage?: number
+ by?: string
+ search?: string
+ pageSize?: number
+ onlyListedCampaigns?: boolean
+ lastDays?: number
+ currentPeriod?: boolean
+ cids?: string[]
+}
+
+interface IProjectLeaderboardResponse {
+ filteredLeaderboard: ITideLeaderboardItem[]
+ nextPage: number
+ leaderboardUserCount: number
+ userPosition: ITideUserPosition
+}
+
+interface ICampaignsResponse {
+ id: string
+ title: string
+ description: string
+ imageUrl: string
+ imagePreviewUrl: string
+ isBanned: boolean
+ startTime: string
+ endTime: string
+ address: string
+ url: string
+ chain: number
+ projectId: number
+ projectLinks: string[]
+ successMessage: string
+ isGasless: boolean
+ isSoulbound: boolean
+ listingStatus: string
+ template: {
+ type: string
+ name: string
+ }
+ isCreationCompleted: boolean
+ externalReward: number
+ createdAt: string
+ updatedAt: string
+ ipfsHash: string
+ participants: number
+ step: number
+ ERC20Reward: unknown
+ pickedByTide: boolean
+}
+
+interface ICampaignResponse extends ICampaignsResponse {
+ numberOfTasks: number
+}
+
+interface IMultipleCampaignsResponse {
+ campaigns: ICampaignsResponse[]
+ totalCampaigns: number
+}
+
+interface INftUserDataResponse {
+ tokenId: string
+ description: string
+ image: string
+ external_url: string
+ attributes: INftAttribute[]
+ userPosition: ITideUserPosition
+}
+
+export class TideApi extends BaseApi {
+ constructor() {
+ super(TIDE_API_BASE_URL)
+ }
+
+ async getProjectLeaderboard(
+ projectId: number,
+ queryParams?: ProjectLeaderboardQueryParams
+ ): Promise {
+ const path = `${TideApiEndpoint.Project}/${projectId}/leaderboard?${
+ queryParams ? buildQueryParametersFromObject(queryParams) : ''
+ }`
+ const response = await this.get(path)
+ return response
+ }
+
+ async getCampaign(campaignId: string): Promise {
+ const path = `${TideApiEndpoint.Campaign}/${campaignId}`
+ const response = await this.get(path)
+ return response
+ }
+
+ async getCampaignsForChain(chainId: number): Promise {
+ const path = `${TideApiEndpoint.Campaign}/chain/${chainId}`
+ const response = await this.get(path)
+ return response
+ }
+
+ async getNftUserData(
+ chainId: number,
+ userAddress: string,
+ contractAddress: string
+ ): Promise {
+ const path = `${TideApiEndpoint.Nft}/${userAddress}/${contractAddress}/${chainId}`
+ const response = await this.get(path)
+ return response
+ }
+}
diff --git a/packages/shared/src/lib/core/tide/constants/index.ts b/packages/shared/src/lib/core/tide/constants/index.ts
new file mode 100644
index 0000000000..2db7fd6afb
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/constants/index.ts
@@ -0,0 +1,2 @@
+export * from './tide-api-base-url.constant'
+export * from './tide-base-url.constant'
diff --git a/packages/shared/src/lib/core/tide/constants/tide-api-base-url.constant.ts b/packages/shared/src/lib/core/tide/constants/tide-api-base-url.constant.ts
new file mode 100644
index 0000000000..f5e5a2acf4
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/constants/tide-api-base-url.constant.ts
@@ -0,0 +1 @@
+export const TIDE_API_BASE_URL = 'https://api-prod.tideprotocol.xyz/public'
diff --git a/packages/shared/src/lib/core/tide/constants/tide-base-url.constant.ts b/packages/shared/src/lib/core/tide/constants/tide-base-url.constant.ts
new file mode 100644
index 0000000000..9ec3f53fbe
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/constants/tide-base-url.constant.ts
@@ -0,0 +1 @@
+export const TIDE_BASE_URL = 'https://tideprotocol.xyz'
diff --git a/packages/shared/src/lib/core/tide/enums/index.ts b/packages/shared/src/lib/core/tide/enums/index.ts
new file mode 100644
index 0000000000..7a04b95b8a
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/enums/index.ts
@@ -0,0 +1,3 @@
+export * from './tide-api-endpoint.enum'
+export * from './tide-listing-status.enum'
+export * from './tide-website-endpoint.enum'
diff --git a/packages/shared/src/lib/core/tide/enums/tide-api-endpoint.enum.ts b/packages/shared/src/lib/core/tide/enums/tide-api-endpoint.enum.ts
new file mode 100644
index 0000000000..3e6a3a4991
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/enums/tide-api-endpoint.enum.ts
@@ -0,0 +1,5 @@
+export enum TideApiEndpoint {
+ Campaign = 'campaign',
+ Project = 'project',
+ Nft = 'nft',
+}
diff --git a/packages/shared/src/lib/core/tide/enums/tide-listing-status.enum.ts b/packages/shared/src/lib/core/tide/enums/tide-listing-status.enum.ts
new file mode 100644
index 0000000000..fb199c3c16
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/enums/tide-listing-status.enum.ts
@@ -0,0 +1,5 @@
+export enum TideListingStatus {
+ Listed = 'LISTED',
+ Unlisted = 'UNLISTED',
+ Requested = 'REQUESTED',
+}
diff --git a/packages/shared/src/lib/core/tide/enums/tide-website-endpoint.enum.ts b/packages/shared/src/lib/core/tide/enums/tide-website-endpoint.enum.ts
new file mode 100644
index 0000000000..2c31603a27
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/enums/tide-website-endpoint.enum.ts
@@ -0,0 +1,4 @@
+export enum TideWebsiteEndpoint {
+ Campaign = 'users/campaign',
+ Project = 'users/spaces',
+}
diff --git a/packages/shared/src/lib/core/tide/index.ts b/packages/shared/src/lib/core/tide/index.ts
new file mode 100644
index 0000000000..6921285864
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/index.ts
@@ -0,0 +1,2 @@
+export * from './constants'
+export * from './enums'
diff --git a/packages/shared/src/lib/core/tide/interfaces/index.ts b/packages/shared/src/lib/core/tide/interfaces/index.ts
new file mode 100644
index 0000000000..534e3ac50b
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/interfaces/index.ts
@@ -0,0 +1,2 @@
+export * from './tide-leaderboard-item.interface'
+export * from './tide-user-position.interface'
diff --git a/packages/shared/src/lib/core/tide/interfaces/tide-leaderboard-item.interface.ts b/packages/shared/src/lib/core/tide/interfaces/tide-leaderboard-item.interface.ts
new file mode 100644
index 0000000000..93c6c65f43
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/interfaces/tide-leaderboard-item.interface.ts
@@ -0,0 +1,8 @@
+export interface ITideLeaderboardItem {
+ address: string
+ taskDone: number
+ rewardClaimed: number
+ position: number
+ xpGained: number
+ totalXp: number
+}
diff --git a/packages/shared/src/lib/core/tide/interfaces/tide-user-position.interface.ts b/packages/shared/src/lib/core/tide/interfaces/tide-user-position.interface.ts
new file mode 100644
index 0000000000..8a88001a0d
--- /dev/null
+++ b/packages/shared/src/lib/core/tide/interfaces/tide-user-position.interface.ts
@@ -0,0 +1,7 @@
+export interface ITideUserPosition {
+ address: string
+ xpEarned: number
+ rewardClaimed: number
+ tasksDone: number
+ position: number
+}
diff --git a/packages/shared/src/lib/core/utils/time.ts b/packages/shared/src/lib/core/utils/time.ts
index 3322eddb1c..5a91e8c5cf 100644
--- a/packages/shared/src/lib/core/utils/time.ts
+++ b/packages/shared/src/lib/core/utils/time.ts
@@ -70,8 +70,14 @@ export function getTimeDifference(lateDate: Date, earlyDate: Date): string {
if (years > 0) {
return `${years}y ${days}d`
- } else if (days > 0 || hours > 0) {
+ } else if (days > 9 || (days > 0 && hours === 0)) {
+ return `${days}d`
+ } else if (days > 0 && hours > 0) {
return `${days}d ${hours}h`
+ } else if (hours > 9 || (hours > 0 && minutes === 0)) {
+ return `${hours}h`
+ } else if (hours > 0 && minutes > 0) {
+ return `${hours}h ${minutes}min`
} else if (minutes > 0) {
return `${minutes}min`
} else if (seconds > 0) {
diff --git a/packages/shared/src/lib/core/utils/url.ts b/packages/shared/src/lib/core/utils/url.ts
index 62914c2f4d..10dd8a7455 100644
--- a/packages/shared/src/lib/core/utils/url.ts
+++ b/packages/shared/src/lib/core/utils/url.ts
@@ -22,3 +22,16 @@ export function cleanUrl(
return cleanedUrl
}
+
+export function buildQueryParametersFromObject(obj: Record): string {
+ return Object.keys(obj)
+ .map(
+ (key) =>
+ `${encodeURIComponent(key)}=${
+ Array.isArray(obj[key])
+ ? encodeURIComponent((obj[key] as unknown[]).join(','))
+ : encodeURIComponent(obj[key] as string | number | boolean)
+ }`
+ )
+ .join('&')
+}
diff --git a/packages/shared/src/lib/features/interfaces/campaigns-features.interface.ts b/packages/shared/src/lib/features/interfaces/campaigns-features.interface.ts
new file mode 100644
index 0000000000..a21cb39fa9
--- /dev/null
+++ b/packages/shared/src/lib/features/interfaces/campaigns-features.interface.ts
@@ -0,0 +1,5 @@
+import { IFeatureFlag } from './feature-flag.interface'
+
+export interface ICampaignsFeatures extends IFeatureFlag {
+ importCampaign: IFeatureFlag
+}
diff --git a/packages/shared/src/lib/features/interfaces/features.interface.ts b/packages/shared/src/lib/features/interfaces/features.interface.ts
index 36fbd8428b..9cf122fdd1 100644
--- a/packages/shared/src/lib/features/interfaces/features.interface.ts
+++ b/packages/shared/src/lib/features/interfaces/features.interface.ts
@@ -10,6 +10,7 @@ import { IWalletFeatures } from './wallet-features.interface'
import { IWalletConnectFeatures } from './wallet-connect-features.interface'
import { OnboardingFeatures } from '../types/onboarding-features.type'
import { IFeatureFlag } from './feature-flag.interface'
+import { ICampaignsFeatures } from './campaigns-features.interface'
export interface IFeatures {
app: IAppFeatures
@@ -24,4 +25,5 @@ export interface IFeatures {
wallet: IWalletFeatures
walletConnect: IWalletConnectFeatures
buySell: IFeatureFlag
+ campaigns: ICampaignsFeatures
}
diff --git a/packages/shared/src/lib/features/interfaces/index.ts b/packages/shared/src/lib/features/interfaces/index.ts
index 100c3c52b3..a356493a55 100644
--- a/packages/shared/src/lib/features/interfaces/index.ts
+++ b/packages/shared/src/lib/features/interfaces/index.ts
@@ -1,4 +1,5 @@
export * from './app-features.interface'
+export * from './campaigns-features.interface'
export * from './collectibles-features.interface'
export * from './contacts-features.interface'
export * from './developer-features.interface'
diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json
index 9d33797fdd..3322cb3656 100644
--- a/packages/shared/src/locales/en.json
+++ b/packages/shared/src/locales/en.json
@@ -599,6 +599,26 @@
}
}
},
+ "campaigns": {
+ "gallery": {
+ "title": "Campaigns",
+ "emptyTitle": "No campaigns found",
+ "noResults": "No results found",
+ "emptyDescription": "Browse and participate in campaigns with Tide",
+ "emptyAction": "Visit Tide"
+ },
+ "details": {
+ "top10": "Top 10 Leaderboard",
+ "myPosition": "My position",
+ "points": "Points",
+ "tasksComplete": "Tasks Complete",
+ "rewardsClaimed": "Rewards Claimed",
+ "project": "Project",
+ "campaign": "Campaign",
+ "nftBadge": "NFT Badge",
+ "noNftBadge": "No NFT Badge Claimed"
+ }
+ },
"governance": {
"votingPower": {
"title": "Voting power",
@@ -1528,7 +1548,13 @@
"methodName": "Method name",
"data": "Data",
"success": "Success",
- "info": "Info"
+ "info": "Info",
+ "active": "Active",
+ "ended": "Ended",
+ "draft": "Draft",
+ "upcoming": "Upcoming",
+ "featured": "Featured",
+ "raffle": "Raffle"
},
"filters":{
"title": "Filters",
@@ -2029,10 +2055,11 @@
"wallet": "Wallet",
"collectibles": "Collectibles",
"governance": "Governance",
+ "campaigns": "Campaigns",
+ "buySell": "Buy & Sell",
"developer": "Developer",
"tokens": "Tokens",
- "settings": "Settings",
- "buySell": "Buy & Sell"
+ "settings": "Settings"
},
"pills": {
"asyncStatus": {
diff --git a/packages/shared/tailwind.config.js b/packages/shared/tailwind.config.js
index 720c3389f6..9fd4cd095d 100644
--- a/packages/shared/tailwind.config.js
+++ b/packages/shared/tailwind.config.js
@@ -36,6 +36,9 @@ module.exports = {
pattern: /^grid-cols-/,
variants: ['sm', 'md', 'lg', 'xl', '2xl'],
},
+ {
+ pattern: /^col-span-/,
+ },
{
pattern: /^rounded-/,
},
diff --git a/yarn.lock b/yarn.lock
index 496d3af65f..ac71cd91f4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -377,10 +377,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
-"@bloomwalletio/ui@0.19.5":
- version "0.19.5"
- resolved "https://npm.pkg.github.com/download/@bloomwalletio/ui/0.19.5/bdd884be8469bc518aa9f52743da726441abc4b0#bdd884be8469bc518aa9f52743da726441abc4b0"
- integrity sha512-28gav+1p9ObsjJIXkyb2PvV2RkP9UPipMvQ4nZoayyyIzMnYt6uFzVHQrJ9OeL57sadE2jjtjixNfw+I8/yFwA==
+"@bloomwalletio/ui@0.19.9":
+ version "0.19.9"
+ resolved "https://npm.pkg.github.com/download/@bloomwalletio/ui/0.19.9/0f0da73f75f1ab1de4be35e27bb7ba8faa5362a0#0f0da73f75f1ab1de4be35e27bb7ba8faa5362a0"
+ integrity sha512-p+aqmsHLiaol29QCbhoGzOyXoSbji/7fOe+uoSj0NCA8umn5UouNAYVtF7UZiwd5pdeZprUPciMEPvGu2g9O+g==
dependencies:
"@floating-ui/dom" "1.4.3"
"@popperjs/core" "2.11.8"
@@ -2765,6 +2765,13 @@
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
+"@types/sanitize-html@^2.9.5":
+ version "2.9.5"
+ resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.9.5.tgz#e8b2214c8afc7bb88d62f9c3bbbc5b4ecc80a25d"
+ integrity sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==
+ dependencies:
+ htmlparser2 "^8.0.0"
+
"@types/secp256k1@^4.0.1":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c"
@@ -4985,11 +4992,25 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-serializer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+ integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ entities "^4.2.0"
+
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
+domelementtype@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
domexception@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
@@ -4997,6 +5018,22 @@ domexception@^4.0.0:
dependencies:
webidl-conversions "^7.0.0"
+domhandler@^5.0.2, domhandler@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+ integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ dependencies:
+ domelementtype "^2.3.0"
+
+domutils@^3.0.1:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
+ integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
+ dependencies:
+ dom-serializer "^2.0.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+
dotenv-expand@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
@@ -5179,7 +5216,7 @@ enhanced-resolve@^5.15.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"
-entities@^4.4.0:
+entities@^4.2.0, entities@^4.4.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
@@ -6548,6 +6585,16 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+htmlparser2@^8.0.0:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21"
+ integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+ entities "^4.4.0"
+
http-cache-semantics@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
@@ -7005,6 +7052,11 @@ is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
+is-plain-object@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+ integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+
is-potential-custom-element-name@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
@@ -8391,7 +8443,7 @@ nano-json-stream-parser@^0.1.2:
resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f"
integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==
-nanoid@^3.1.31, nanoid@^3.3.6:
+nanoid@^3.1.31, nanoid@^3.3.6, nanoid@^3.3.7:
version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
@@ -8790,6 +8842,11 @@ parse-json@^5.2.0:
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
+parse-srcset@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
+ integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==
+
parse5@^7.0.0, parse5@^7.1.1:
version "7.1.2"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
@@ -9097,6 +9154,15 @@ postcss@8.4.31, postcss@^8.4.21, postcss@^8.4.23:
picocolors "^1.0.0"
source-map-js "^1.0.2"
+postcss@^8.3.11:
+ version "8.4.33"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
+ integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
prebuild-install@^7.0.1, prebuild-install@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
@@ -9714,6 +9780,18 @@ sanitize-filename@^1.6.3:
dependencies:
truncate-utf8-bytes "^1.0.0"
+sanitize-html@^2.11.0:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.11.0.tgz#9a6434ee8fcaeddc740d8ae7cd5dd71d3981f8f6"
+ integrity sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==
+ dependencies:
+ deepmerge "^4.2.2"
+ escape-string-regexp "^4.0.0"
+ htmlparser2 "^8.0.0"
+ is-plain-object "^5.0.0"
+ parse-srcset "^1.0.2"
+ postcss "^8.3.11"
+
sass@1.69.5:
version "1.69.5"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde"