Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release-build-tag-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ jobs:
artifacts: 'platforms/web/build/ott-web-app-build-*.tar.gz, platforms/web/build/ott-web-app-build-*.zip'
tag: v${{ steps.package-version.outputs.current-version }}
bodyFile: '.github/RELEASE_BODY_TEMPLATE.md'
token: ${{ secrets.github_token }}
token: ${{ secrets.GITHUB_TOKEN }}
18 changes: 12 additions & 6 deletions .github/workflows/release-create-release-candidate-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,38 @@ on:

permissions:
contents: write
pull-requests: write

env:
GH_USER: github-actions[bot]
GH_EMAIL: github-actions[bot]@users.noreply.github.com

jobs:
create-release-branch:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.ACTION_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
ref: release
# It would be easier to create the branch from develop,
# but unfortunately the script that creates the PR's only considers commits added during the action run
- name: Merge latest from target branch
run: |
git config --global user.name 'Release Script'
git config --global user.email 'ott-release-script@jwplayer.com'
git config user.name "${{ env.GH_USER }}"
git config user.email "${{ env.GH_EMAIL }}"
git fetch origin ${{ github.ref_name }}
git merge origin/${{ github.ref_name }}
yarn && yarn workspace @jwp/ott-web run i18next
env:
GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Generate changelog
id: changelog
uses: TriPSs/conventional-changelog-action@v5.1.0
with:
github-token: ${{ secrets.ACTION_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
release-count: 0
skip-tag: true
git-push: false
Expand All @@ -42,7 +48,7 @@ jobs:
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.ACTION_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
title: Release Candidate - ${{ steps.changelog.outputs.tag }}${{ github.ref_name != 'develop' && ' (Hotfix)' || ''}}
base: release
branch: ${{ github.ref_name == 'develop' && 'release-candidate' || 'hotfix-release-candidate' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-merge-back.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
from_branch: release
target_branch: develop
message: 'chore: merge release to develop'
github_token: ${{ secrets.ACTION_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
## [6.10.0](https://github.com/jwplayer/ott-web-app/compare/v6.9.0...v6.10.0) (2025-04-09)


### Features

* add external payment methods and mapping ([86dce68](https://github.com/jwplayer/ott-web-app/commit/86dce687952cb597060a6fb2809d4bbbc2073626))
* card subtitle support ([5b83938](https://github.com/jwplayer/ott-web-app/commit/5b8393820120f047605e34a88e4ab7325bc7a862))
* cookie banner and defer gtm load ([afa916d](https://github.com/jwplayer/ott-web-app/commit/afa916d5196e3dae91b2f9e33ed6d3a62b21212b))
* **entitlement:** add access type to generic entitlement payload ([4e7ced7](https://github.com/jwplayer/ott-web-app/commit/4e7ced71b8ba8aebd2a427dd00f0a940b2f32d07))
* **entitlement:** remove lock icon from cards to hub screens ([1e3c3dc](https://github.com/jwplayer/ott-web-app/commit/1e3c3dc181f849f08a72005acca577b0e2ae98e0))
* **home:** hero shelf styling improvements ([8bc0835](https://github.com/jwplayer/ott-web-app/commit/8bc0835191ddd9689baabfb644186208cf00de05))
* **i18n:** add nl/fr/de languages ([788497d](https://github.com/jwplayer/ott-web-app/commit/788497d54f21a8f6a039bca0f9a154dfc5d13164))
* movie screen by composition ([bba7337](https://github.com/jwplayer/ott-web-app/commit/bba73378fc64ea364b4c00143f1d14d16830b526))
* **player:** add support for custom chromecast receiver ([8cff147](https://github.com/jwplayer/ott-web-app/commit/8cff14793c83c09f17e85bd51a8c341ba41e9ced))
* show hub items in grid layout when single shelf ([fc9c7a3](https://github.com/jwplayer/ott-web-app/commit/fc9c7a3c4a4e917d673f9ccd4eb41b0a31f40497))


### Bug Fixes

* allow playing signed trailer items ([9c1c967](https://github.com/jwplayer/ott-web-app/commit/9c1c9673265dda6bb2b75256a6c57b5536ae9af6))
* catch error when retrieving the offers fail ([28b300a](https://github.com/jwplayer/ott-web-app/commit/28b300a3fbd46ab8420bf18dfaa8fa65a9296298))
* **entitlement:** refresh token before calling entitlement ([f4bc386](https://github.com/jwplayer/ott-web-app/commit/f4bc386cd07d0316360293c2e1c62675f2e70938))
* hero shelf clipping on safari browsers ([c5ea54f](https://github.com/jwplayer/ott-web-app/commit/c5ea54f7436ccc2e44cf03a5a27f113d8570f0e2))
* **home:** decrease shelf title font size on mobile ([741b9d5](https://github.com/jwplayer/ott-web-app/commit/741b9d5e61638600256547acb85dfcf65aacbdec))
* **i18n:** sync translations ([#675](https://github.com/jwplayer/ott-web-app/issues/675)) ([189a27f](https://github.com/jwplayer/ott-web-app/commit/189a27fcd63d5b51276d6668ad65c1d83e704e1f))
* **i18n:** update i18n keys ([#683](https://github.com/jwplayer/ott-web-app/issues/683)) ([673b199](https://github.com/jwplayer/ott-web-app/commit/673b199f9febd02b5f580c459de3a4a5696781ec))
* infinite scrolling ([b26d2f1](https://github.com/jwplayer/ott-web-app/commit/b26d2f1aa681c1665b51ebb175598d088282ba64))
* prevent google fonts preload when using system font ([4a70728](https://github.com/jwplayer/ott-web-app/commit/4a707287733678d9a729b9725106a4c04b8dd18a))
* support newline break in markdown content ([77b66b7](https://github.com/jwplayer/ott-web-app/commit/77b66b74380115d76922230781d4191d9fb7e369))
* video details buttons wrapping ([82f2143](https://github.com/jwplayer/ott-web-app/commit/82f2143fce74353645ed069cc6a0be6a8f535792))

## [6.9.0](https://github.com/jwplayer/ott-web-app/compare/v6.8.0...v6.9.0) (2024-12-17)


Expand Down
6 changes: 3 additions & 3 deletions configs/eslint-config-jwp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"test": "exit 0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.17.0",
"@typescript-eslint/parser": "^8.17.0",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"confusing-browser-globals": "^1.0.11",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^4.6.2"
}
}
2 changes: 1 addition & 1 deletion configs/postcss-config-jwp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"test": "exit 0"
},
"devDependencies": {
"postcss": "^8.4.49",
"postcss": "^8.5.3",
"postcss-import": "^14.1.0",
"postcss-scss": "^4.0.9",
"stylelint": "^15.11.0"
Expand Down
2 changes: 1 addition & 1 deletion configs/stylelint-config-jwp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"devDependencies": {
"stylelint": "^15.11.0",
"stylelint-config-recommended-scss": "^13.1.0",
"stylelint-declaration-strict-value": "^1.10.6",
"stylelint-declaration-strict-value": "^1.10.11",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^5.3.2"
}
Expand Down
22 changes: 16 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ each shelf separately.

**content[].contentId**

The eight-character Playlists IDs from the JW Player dashboard. These IDs populate the video "shelves" on your site. *
*contentId** is not required if you use `continue_watching` or `favorites` **type**.
The eight-character Playlists IDs from the JW Player dashboard. These IDs populate the video "shelves" on your site. \*
\*contentId** is not required if you use `continue_watching` or `favorites` **type\*\*.

---

Expand Down Expand Up @@ -145,6 +145,16 @@ You can change the background color of the shelf with the help of this property

---

**content[].filterTags** (optional)

You can optionally define a list of comma separated labels which are used to filter shelves.

The OTT Web App offers a demonstration of its filtering capabilities. This example can be enabled by configuring the `enableLabelsFiltering` custom parameter.

Filtering by device type, country, and day of week is supported. See the `Home.tsx` file for a code example.

---

**styling**

Use the `styling` object to define extra styles for your application.
Expand Down Expand Up @@ -305,11 +315,11 @@ Note, this setting is ignored if Cleeng is not enabled (i.e. there is not Cleeng
**integrations.cleeng.monthlyOffer** (optional)

If Cleeng is enabled, and you want to show the Payments and Subscription functionality, you need to include at least one
offer ID (either this or the yearly offer property).
The application uses this ID to map to an offer that you've configured in your Cleeng environment under Offers to
represent your monthly subscription.
offer ID (either this or the yearly offer property).
The application uses this ID to map to an offer that you've configured in your Cleeng environment under Offers to
represent your monthly subscription.
Note that only the data used from the Cleeng offer is the price, the free days, and the free period,
and the app does not verify if the offer length is actually monthly.
and the app does not verify if the offer length is actually monthly.
If no monthly or yearly offer is configured, the Payments section will not be shown.

---
Expand Down
5 changes: 2 additions & 3 deletions knip.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const config: KnipConfig = {
'packages/ui-react': {
entry: ['src/**/*'],
ignoreDependencies: [
'@types/dompurify', // Somehow this is not recognised
'sass-embedded', // Used in Vite
'postcss-config-jwp', // Used in postcss.config
],
Expand All @@ -29,16 +30,14 @@ const config: KnipConfig = {
ignoreDependencies: [
'@codeceptjs/allure-legacy',
'@codeceptjs/configure', // Used in e2e tests
'@babel/plugin-proposal-decorators', // Used to build with decorators for ioc resolution
'@babel/core', // Required peer dependency for babel plugins
'@jwp/ott-testing', // Used in e2e testing
'@types/luxon', // Used in tests
'babel-plugin-transform-typescript-metadata', // Used to build with decorators for ioc resolution
'core-js', // Conditionally imported at build time
'eslint-plugin-codeceptjs', // Used by apps/web/test-e2e/.eslintrc.cjs
'i18next-parser',
'luxon', // Used in tests
'playwright', // Used in test configs
'axe-playwright', // Used in test configs
'tsconfig-paths', // Used for e2e test setup
],
},
Expand Down
32 changes: 14 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jwp/ott",
"version": "6.9.0",
"version": "6.10.0",
"private": true,
"license": "Apache-2.0",
"repository": "https://github.com/jwplayer/ott-web-app.git",
Expand Down Expand Up @@ -32,37 +32,33 @@
"pre-commit": "yarn depcheck && lint-staged",
"prepare": "husky install",
"test": "TZ=UTC LC_ALL=en_US.UTF-8 vitest run",
"test-update": "TZ=UTC LC_ALL=en_US.UTF-8 vitest run -u",
"test-watch": "TZ=UTC LC_ALL=en_US.UTF-8 vitest",
"web": "yarn --cwd platforms/web",
"access-bridge": "yarn --cwd platforms/access-bridge"
},
"devDependencies": {
"@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.6.0",
"@types/node": "^22.10.1",
"@babel/core": "^7.26.10",
"@babel/plugin-proposal-decorators": "^7.25.9",
"@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.8.0",
"@types/node": "^22.13.10",
"csv-parse": "^5.6.0",
"eslint": "^8.57.1",
"eslint-config-jwp": "*",
"husky": "^6.0.0",
"i18next-parser-workspaces": "^0.2.0",
"knip": "^5.39.1",
"lint-staged": "^15.2.10",
"i18next-parser-workspaces": "^0.2.1",
"knip": "^5.61.2",
"lint-staged": "^15.4.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"read": "^2.1.0",
"ts-node": "^10.9.2",
"typescript": "5.7.2",
"vitest": "^2.1.8"
"typescript": "5.8.3",
"vitest": "^3.0.8"
},
"resolutions": {
"axios": "^0.29.0",
"cheerio": "1.0.0-rc.12",
"codeceptjs/**/fast-xml-parser": "^4.5.0",
"eslint/**/cross-spawn": "^7.0.6",
"lint-staged/**/cross-spawn": "^7.0.6",
"npm-run-all/**/cross-spawn": "^6.0.6",
"micromatch": ">=4.0.8",
"vitest/**/vite": "^5.4.11",
"ws": ">=5.2.4"
"axios": "^0.30.0",
"**/minimatch/brace-expansion": "^1.1.12"
}
}
12 changes: 6 additions & 6 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@
"dependencies": {
"@inplayer-org/inplayer.js": "^3.13.28",
"date-fns": "^4.1.0",
"fast-xml-parser": "^4.5.0",
"i18next": "^24.0.5",
"fast-xml-parser": "^4.5.3",
"i18next": "^24.2.2",
"ini": "^3.0.1",
"inversify": "^6.1.5",
"jwt-decode": "^4.0.0",
"lodash.merge": "^4.6.2",
"react-i18next": "^15.1.3",
"react-i18next": "^15.4.1",
"reflect-metadata": "^0.2.2",
"yup": "^1.4.0",
"yup": "^1.6.1",
"zustand": "^4.5.5"
},
"devDependencies": {
"@jwp/ott-testing": "*",
"@types/ini": "^1.3.34",
"@types/lodash.merge": "^4.6.9",
"eslint-config-jwp": "*",
"jsdom": "^22.1.0",
"jsdom": "^26.0.0",
"timezone-mock": "^1.3.6",
"vi-fetch": "^0.8.0",
"vitest": "^2.1.8"
"vitest": "^3.0.8"
}
}
2 changes: 2 additions & 0 deletions packages/common/src/controllers/CheckoutController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ export default class CheckoutController {
return response.responseData;
};

finalizeStripePpvPayment = (paymentIntent: string) => this.checkoutService?.finalizeStripePpvPayment?.({ paymentIntent });

paypalPayment = async ({
successUrl,
waitingUrl,
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/services/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ export default class ApiService {
* Transform incoming content lists
*/
protected transformContentList = (contentList: ContentList, language: string): Playlist => {
const { list, ...rest } = contentList;
const { list, id, ...rest } = contentList;

const playlist: Playlist = { ...rest, playlist: [] };
const playlist: Playlist = { ...rest, feedid: id, playlist: [] };

playlist.playlist = list.map((item) => {
const { custom_params, media_id, description, tags, ...rest } = item;
Expand Down
3 changes: 3 additions & 0 deletions packages/common/src/services/integrations/CheckoutService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
GetDirectPostCardPayment,
GetEntitlements,
GetFinalizeAdyenPayment,
GetFinalizeStripePpvPayment,
GetInitialAdyenPayment,
GetOffer,
GetOffers,
Expand Down Expand Up @@ -54,6 +55,8 @@ export default abstract class CheckoutService {

abstract finalizeAdyenPayment?: GetFinalizeAdyenPayment;

abstract finalizeStripePpvPayment?: GetFinalizeStripePpvPayment;

abstract updatePaymentMethodWithPayPal?: UpdatePaymentWithPayPal;

abstract deletePaymentMethod?: DeletePaymentMethod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ export default class CleengCheckoutService extends CheckoutService {
finalizeAdyenPayment: GetFinalizeAdyenPayment = async (payload) =>
this.cleengService.post('/connectors/adyen/initial-payment/finalize', JSON.stringify(payload), { authenticate: true });

finalizeStripePpvPayment: undefined;

updatePaymentMethodWithPayPal: UpdatePaymentWithPayPal = async (payload) => {
return this.cleengService.post('/connectors/paypal/v1/payment_details/tokens', JSON.stringify(payload), { authenticate: true });
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,12 @@ export default class JWPCheckoutService extends CheckoutService {
customerCurrency: offer.currency,
offerTitle: offer.description,
active: true,
period: offer.access_type.period === 'month' && offer.access_type.quantity === 12 ? 'year' : offer.access_type.period,
freePeriods: offer.trial_period ? 1 : 0,
period:
offer.access_type.period === 'month' && offer.access_type.quantity === 12 ? 'year' : (offer.access_type.period as 'day' | 'week' | 'month' | 'year'),
planSwitchEnabled: offer.item.plan_switch_enabled ?? false,

// trial period is always assumed to be in days
freeDays: offer.trial_period?.quantity || 0,
} as Offer;
};

Expand Down Expand Up @@ -303,6 +306,14 @@ export default class JWPCheckoutService extends CheckoutService {
}
};

finalizeStripePpvPayment = async ({ paymentIntent }: { paymentIntent: string }) => {
try {
await this.apiService.post<CommonResponse>('/payments', { pi_id: paymentIntent }, { withAuthentication: true });
} catch {
throw new Error('Failed to confirm payment');
}
};

getSubscriptionSwitches = undefined;

getOrder = undefined;
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/utils/configSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const contentSchema: ObjectSchema<Content> = object({
backgroundColor: string().nullable().optional(),
type: string().oneOf(['playlist', 'continue_watching', 'favorites', 'content_list']).required(),
custom: object().optional(),
filterTags: string().optional(),
}).defined();

const menuSchema: ObjectSchema<Menu> = object().shape({
Expand Down
16 changes: 16 additions & 0 deletions packages/common/src/utils/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,19 @@ export const is12HourClock = () => {
const date = new Date(Date.UTC(2022, 6, 20, 18, 0, 0));
return /am|pm/.test(date.toLocaleTimeString());
};

export const formatItemDate = (date?: string | null, locale?: string) => {
if (!date) return null;

if (date.length <= 4) return date;

try {
const [day, month, year] = date.split('-');
const dateObj = new Date(`${year}-${month}-${day}`);
if (dateObj.toString() === 'Invalid Date') return date; // fallback to original string

return dateObj.toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric' });
} catch (error: unknown) {
return date; // fallback to original string
}
};
Loading
Loading