@shopify/app-bridge-host
Advanced tools
Comparing version 1.3.1-alpha.3 to 1.3.1-alpha.4
@@ -6,2 +6,19 @@ # Change Log | ||
## [1.3.1-alpha.4](https://github.com/Shopify/app-bridge/compare/v1.3.1-alpha.3...v1.3.1-alpha.4) (2019-05-17) | ||
### Bug Fixes | ||
* **message-transport:** add `frameWindow` to identify source of dispatch ([#885](https://github.com/Shopify/app-bridge/issues/885)) ([4558215](https://github.com/Shopify/app-bridge/commit/4558215)) | ||
* **reducers:** fixed default values for feature detection actions ([#924](https://github.com/Shopify/app-bridge/issues/924)) ([03c81ae](https://github.com/Shopify/app-bridge/commit/03c81ae)) | ||
### Features | ||
* **fullscreen:** add reducer to app-bridge-host ([163213c](https://github.com/Shopify/app-bridge/commit/163213c)) | ||
## [1.3.1-alpha.3](https://github.com/Shopify/app-bridge/compare/v1.3.1-alpha.2...v1.3.1-alpha.3) (2019-05-15) | ||
@@ -8,0 +25,0 @@ |
@@ -63,2 +63,5 @@ "use strict"; | ||
} | ||
if (to.frameWindow !== event.source) { | ||
return; | ||
} | ||
for (var _i = 0, subscribers_1 = subscribers; _i < subscribers_1.length; _i++) { | ||
@@ -65,0 +68,0 @@ var listener = subscribers_1[_i]; |
{ | ||
"name": "@shopify/app-bridge-host", | ||
"version": "1.3.1-alpha.3", | ||
"version": "1.3.1-alpha.4", | ||
"types": "index.d.ts", | ||
@@ -57,3 +57,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"@shopify/app-bridge": "^1.3.1-alpha.3", | ||
"@shopify/app-bridge": "^1.3.1-alpha.4", | ||
"@shopify/javascript-utilities": "^2.3.0", | ||
@@ -67,3 +67,3 @@ "hoist-non-react-statics": "^3.1.0", | ||
}, | ||
"gitHead": "dad198b0a9b010c07c9e62de21046baadf012d6a" | ||
"gitHead": "cbcd89171547f8602a2c61eda909bc6a17b8f761" | ||
} |
import { compose, Middleware as ReduxMiddleware } from 'redux'; | ||
import { buildMiddleware } from '../Middleware'; | ||
import { AppBridgeStore } from './reducers'; | ||
export interface Store { | ||
appBridge: AppBridgeStore; | ||
} | ||
export * from './middlewares'; | ||
export * from './reducers'; | ||
interface DevToolsOptions { | ||
@@ -15,5 +14,4 @@ name?: string; | ||
} | ||
export default function createStore(middlewares?: Array<ReturnType<typeof buildMiddleware> | ReduxMiddleware>): import("redux").Store<Store, import("redux").AnyAction> & { | ||
export default function createStore(middlewares?: Array<ReturnType<typeof buildMiddleware> | ReduxMiddleware>): import("redux").Store<AppBridgeStore, import("redux").AnyAction> & { | ||
dispatch: {}; | ||
}; | ||
export {}; |
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -10,2 +13,4 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var reducers_1 = __importDefault(require("./reducers")); | ||
__export(require("./middlewares")); | ||
__export(require("./reducers")); | ||
function createStore(middlewares) { | ||
@@ -12,0 +17,0 @@ if (middlewares === void 0) { middlewares = []; } |
export { default as buildMobileMiddleware, preCorrectionMobileMiddleware } from './middleware'; | ||
export * from './helpers'; |
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -6,1 +9,2 @@ var middleware_1 = require("./middleware"); | ||
exports.preCorrectionMobileMiddleware = middleware_1.preCorrectionMobileMiddleware; | ||
__export(require("./helpers")); |
@@ -1,6 +0,19 @@ | ||
import { FeaturesState, FeaturesAction } from '@shopify/app-bridge'; | ||
import { FeaturesState, FeaturesAction, FeaturePermission } from '@shopify/app-bridge'; | ||
import * as Actions from '@shopify/app-bridge/actions'; | ||
import { LegacyFeaturesAction, LegacyUpdateAction, UpdateAction } from './actions'; | ||
import { LegacyUpdateAction, UpdateAction } from './actions'; | ||
export declare const defaultFeaturesStore: FeaturesState; | ||
export declare function transformLegacyFeature(featuresAction: LegacyFeaturesAction): FeaturesAction; | ||
export default function featuresReducer(state: FeaturesState | undefined, action: UpdateAction | LegacyUpdateAction | Actions.AnyAction): FeaturesState; | ||
/** | ||
* Map an action enum to the given permission | ||
* @public | ||
* @param actions - the actions enum | ||
* @param value - the permission to set | ||
* @returns an object with keys mapped to the actions and value set to the given permission | ||
*/ | ||
export declare function setFeature<A extends Object>(actions: A, value?: boolean | FeaturePermission): FeaturesAction; | ||
/** | ||
* Returns the updated feature state | ||
* @param state - the current state | ||
* @param action - the update action with partial features in the payload | ||
* @internal | ||
* */ | ||
export default function featuresReducer(state: FeaturesState<Record<Actions.Group, FeaturesAction>> | undefined, action: UpdateAction | LegacyUpdateAction | Actions.AnyAction): FeaturesState; |
@@ -24,2 +24,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _a; | ||
var app_bridge_1 = require("@shopify/app-bridge"); | ||
@@ -30,9 +31,24 @@ var Actions = __importStar(require("@shopify/app-bridge/actions")); | ||
var APP_REDIRECT_KEY = validator_1.getPermissionKey(Actions.Redirect.ActionType.APP); | ||
var DEFAULT_FEATURES_STATE = (_a = {}, | ||
_a[Actions.Features.Action.UPDATE] = { | ||
Dispatch: false, | ||
Subscribe: true, | ||
}, | ||
_a[Actions.Features.Action.REQUEST] = { | ||
Dispatch: true, | ||
Subscribe: false, | ||
}, | ||
_a[Actions.Features.Action.REQUEST_UPDATE] = { | ||
Dispatch: false, | ||
Subscribe: true, | ||
}, | ||
_a); | ||
exports.defaultFeaturesStore = getDefaultFeatures(); | ||
function getDefaultFeatures() { | ||
var _a, _b, _c, _d; | ||
var excludeGroups = [Actions.Group.Cart, Actions.Group.Camera]; | ||
var _a, _b, _c, _d, _e; | ||
var excludeGroups = [Actions.Group.Cart, Actions.Group.Camera, Actions.Group.Fullscreen]; | ||
var overrideFeatures = (_a = {}, | ||
_a[app_bridge_1.Context.Modal] = (_b = {}, | ||
_b[Actions.Group.Print] = undefined, | ||
_b[Actions.Group.Print] = setFeature(getGroupActionType(Actions.Group.Print), false), | ||
_b[Actions.Group.Features] = DEFAULT_FEATURES_STATE, | ||
_b[Actions.Group.Navigation] = (_c = {}, | ||
@@ -45,2 +61,5 @@ _c[APP_REDIRECT_KEY] = { | ||
_b), | ||
_a[app_bridge_1.Context.Main] = (_d = {}, | ||
_d[Actions.Group.Features] = DEFAULT_FEATURES_STATE, | ||
_d), | ||
_a); | ||
@@ -52,12 +71,16 @@ var groups = Object.keys(Actions.Group) | ||
var castedGroup = group; | ||
if (excludeGroups.includes(castedGroup)) { | ||
return acc; | ||
} | ||
return __assign({}, acc, (_a = {}, _a[castedGroup] = setFeature(getGroupActionType(group), true), _a)); | ||
return __assign({}, acc, (_a = {}, _a[castedGroup] = setFeature(getGroupActionType(castedGroup), !excludeGroups.includes(castedGroup)), _a)); | ||
}, {}); | ||
return merge_1.default((_d = {}, | ||
_d[app_bridge_1.Context.Main] = groups, | ||
_d[app_bridge_1.Context.Modal] = groups, | ||
_d), overrideFeatures); | ||
return merge_1.default((_e = {}, | ||
_e[app_bridge_1.Context.Main] = groups, | ||
_e[app_bridge_1.Context.Modal] = groups, | ||
_e), overrideFeatures); | ||
} | ||
/** | ||
* Map an action enum to the given permission | ||
* @public | ||
* @param actions - the actions enum | ||
* @param value - the permission to set | ||
* @returns an object with keys mapped to the actions and value set to the given permission | ||
*/ | ||
function setFeature(actions, value) { | ||
@@ -77,22 +100,5 @@ if (value === void 0) { value = false; } | ||
} | ||
exports.setFeature = setFeature; | ||
function getGroupActionType(group) { | ||
switch (group) { | ||
case Actions.Group.Button: | ||
return Actions.Button.ActionType; | ||
case Actions.Group.ButtonGroup: | ||
return Actions.ButtonGroup.ActionType; | ||
case Actions.Group.Cart: | ||
return Actions.Cart.ActionType; | ||
case Actions.Group.Camera: | ||
return Actions.Camera.ActionType; | ||
case Actions.Group.Error: | ||
return Actions.Error.ActionType; | ||
case Actions.Group.Features: | ||
return Actions.Features.ActionType; | ||
case Actions.Group.Loading: | ||
return Actions.Loading.ActionType; | ||
case Actions.Group.Modal: | ||
return Actions.Modal.ActionType; | ||
case Actions.Group.Print: | ||
return Actions.Print.ActionType; | ||
case Actions.Group.Navigation: | ||
@@ -102,8 +108,4 @@ return __assign({}, Actions.History.ActionType, Actions.Redirect.ActionType); | ||
return Actions.ResourcePicker.ActionType; | ||
case Actions.Group.TitleBar: | ||
return Actions.TitleBar.ActionType; | ||
case Actions.Group.Toast: | ||
return Actions.Toast.ActionType; | ||
default: | ||
return {}; | ||
return Actions[group].ActionType; | ||
} | ||
@@ -121,3 +123,8 @@ } | ||
} | ||
exports.transformLegacyFeature = transformLegacyFeature; | ||
/** | ||
* Returns the updated feature state | ||
* @param state - the current state | ||
* @param action - the update action with partial features in the payload | ||
* @internal | ||
* */ | ||
function featuresReducer(state, action) { | ||
@@ -145,3 +152,2 @@ if (state === void 0) { state = exports.defaultFeaturesStore; } | ||
function transformToNewPayload(payload) { | ||
var newPayload = {}; | ||
return Object.keys(payload).reduce(function (acc, group) { | ||
@@ -154,3 +160,3 @@ var _a; | ||
return acc; | ||
}, newPayload); | ||
}, {}); | ||
} |
@@ -12,2 +12,6 @@ import { AppBridgeActionCreatorsMap } from './appBridge'; | ||
import { StaffMemberStore, StaffMemberActionCreatorsMap } from './staffMember'; | ||
/** | ||
* The interface for the app state | ||
* @public | ||
*/ | ||
export interface Store { | ||
@@ -25,3 +29,8 @@ appInfo: AppInfoStore; | ||
isLegacy: boolean; | ||
isFullscreen: boolean; | ||
} | ||
/** | ||
* The interface for the actions available to the app | ||
* @public | ||
*/ | ||
export interface Actions { | ||
@@ -39,5 +48,13 @@ appInfo: AppInfoActionCreatorsMap; | ||
} | ||
/** | ||
* The interface for the app's dispatch props | ||
* @public | ||
*/ | ||
export interface DispatchProps { | ||
actions: Actions; | ||
} | ||
/** | ||
* The interface for the app's default state | ||
* @internal | ||
*/ | ||
export declare const defaultStore: { | ||
@@ -47,3 +64,3 @@ appInfo: { | ||
}; | ||
features: FeaturesState; | ||
features: FeaturesState<Record<import("@shopify/app-bridge/actions").Group, import("@shopify/app-bridge").FeaturesAction>>; | ||
toast: {}; | ||
@@ -63,3 +80,8 @@ loading: { | ||
isLegacy: boolean; | ||
isFullscreen: boolean; | ||
}; | ||
/** | ||
* The combined Redux reducers | ||
* @internal | ||
*/ | ||
export default function getReducers(): import("redux").Reducer<Store, import("redux").AnyAction>; |
@@ -20,5 +20,2 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -37,3 +34,8 @@ var redux_1 = require("redux"); | ||
var staffMember_1 = __importStar(require("./staffMember")); | ||
var legacy_1 = __importDefault(require("./legacy")); | ||
var legacy_1 = __importStar(require("./legacy")); | ||
var fullscreen_1 = __importStar(require("./fullscreen")); | ||
/** | ||
* The interface for the app's default state | ||
* @internal | ||
*/ | ||
exports.defaultStore = { | ||
@@ -50,3 +52,4 @@ appInfo: appInfo_1.defaultAppInfoStore, | ||
staffMember: staffMember_1.defaultStaffMemberStore, | ||
isLegacy: false, | ||
isLegacy: legacy_1.defaultLegacyStore, | ||
isFullscreen: fullscreen_1.defaultFullscreenStore, | ||
}; | ||
@@ -60,3 +63,8 @@ var appStateReducers = utilities_1.wrapReducers({ | ||
navigation: navigation_1.default, | ||
isFullscreen: fullscreen_1.default, | ||
}, utilities_1.resetStateReducer); | ||
/** | ||
* The combined Redux reducers | ||
* @internal | ||
*/ | ||
function getReducers() { | ||
@@ -63,0 +71,0 @@ return redux_1.combineReducers(utilities_1.wrapReducers(__assign({ appInfo: appInfo_1.default, |
@@ -15,3 +15,3 @@ "use strict"; | ||
var actions_1 = require("@shopify/app-bridge/actions"); | ||
var validate_1 = require("./validate"); | ||
var modal_1 = require("@shopify/app-bridge/validate/actions/modal"); | ||
var actions_2 = require("./actions"); | ||
@@ -25,3 +25,3 @@ exports.defaultModalStore = { open: false }; | ||
var castAction = action; | ||
if (validate_1.validateAction(castAction)) { | ||
if (modal_1.validateAction(castAction)) { | ||
return state; | ||
@@ -28,0 +28,0 @@ } |
@@ -18,2 +18,1 @@ "use strict"; | ||
__export(require("./actions")); | ||
__export(require("./types")); |
@@ -16,3 +16,2 @@ "use strict"; | ||
var navigation_1 = require("@shopify/app-bridge/validate/actions/navigation"); | ||
var types_1 = require("./types"); | ||
var actions_2 = require("./actions"); | ||
@@ -34,3 +33,3 @@ exports.defaultNavigationStore = {}; | ||
type: getUpdateActionType(action.type), | ||
target: types_1.TARGET_APP, | ||
target: actions_1.Redirect.Action.APP, | ||
payload: payload, | ||
@@ -48,3 +47,3 @@ }, | ||
var payload = castAction.payload; | ||
var target = action.type === actions_1.Redirect.ActionType.REMOTE ? types_1.TARGET_REMOTE : types_1.TARGET_ADMIN; | ||
var target = getUpdateActionTarget(action.type); | ||
return { | ||
@@ -67,7 +66,16 @@ updateAction: { | ||
case actions_1.History.ActionType.REPLACE: | ||
return types_1.HISTORY_REPLACE; | ||
return actions_1.History.Action.REPLACE; | ||
case actions_1.History.ActionType.PUSH: | ||
return types_1.HISTORY_PUSH; | ||
return actions_1.History.Action.PUSH; | ||
} | ||
} | ||
function getUpdateActionTarget(type) { | ||
switch (type) { | ||
case actions_1.Redirect.ActionType.ADMIN_PATH: | ||
return actions_1.Redirect.Action.ADMIN_PATH; | ||
case actions_1.Redirect.ActionType.ADMIN_SECTION: | ||
return actions_1.Redirect.Action.ADMIN_SECTION; | ||
} | ||
return actions_1.Redirect.Action.REMOTE; | ||
} | ||
exports.default = navigationReducer; |
@@ -1,9 +0,4 @@ | ||
import { Redirect } from '@shopify/app-bridge/actions'; | ||
export declare const TARGET_APP = "APP"; | ||
export declare const TARGET_ADMIN = "ADMIN"; | ||
export declare const TARGET_REMOTE = "REMOTE"; | ||
export declare const HISTORY_PUSH = "PUSH"; | ||
export declare const HISTORY_REPLACE = "REPLACE"; | ||
export declare type UpdateActionType = typeof HISTORY_PUSH | typeof HISTORY_REPLACE; | ||
export declare type UpdateActionTarget = typeof TARGET_APP | typeof TARGET_ADMIN | typeof TARGET_REMOTE; | ||
import { Redirect, History } from '@shopify/app-bridge/actions'; | ||
export declare type UpdateActionType = typeof History.Action.PUSH | typeof History.Action.REPLACE; | ||
export declare type UpdateActionTarget = typeof Redirect.Action.APP | typeof Redirect.Action.ADMIN_PATH | typeof Redirect.Action.ADMIN_SECTION | typeof Redirect.Action.REMOTE; | ||
export interface UpdateAction { | ||
@@ -10,0 +5,0 @@ type?: UpdateActionType; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.TARGET_APP = 'APP'; | ||
exports.TARGET_ADMIN = 'ADMIN'; | ||
exports.TARGET_REMOTE = 'REMOTE'; | ||
exports.HISTORY_PUSH = 'PUSH'; | ||
exports.HISTORY_REPLACE = 'REPLACE'; |
@@ -1,3 +0,16 @@ | ||
import appBridgeReducer from './embeddedApp/appBridge'; | ||
export { Store as AppBridgeStore } from './embeddedApp'; | ||
import { Application } from '../../types'; | ||
import { DispatchProps, Store } from './embeddedApp'; | ||
import appBridgeReducer, { appBridgeActionCreatorsMap, AppBridgeStore, StoreReadyAction } from './embeddedApp/appBridge'; | ||
import { appInfoActionCreatorsMap, AppInfoStore } from './embeddedApp/appInfo'; | ||
import { featuresActionCreatorsMap, FeaturesState } from './embeddedApp/features'; | ||
import { loadingActionCreatorsMap, LoadingStore } from './embeddedApp/loading'; | ||
import { modalActionCreatorsMap, ModalStore } from './embeddedApp/modal'; | ||
import { navigationActionCreatorsMap, NavigationStore } from './embeddedApp/navigation'; | ||
import { resourcePickerActionCreatorsMap, ResourcePickerStore } from './embeddedApp/resourcePicker'; | ||
import { staffMemberActionCreatorsMap, StaffMemberStore } from './embeddedApp/staffMember'; | ||
import { titleBarActionCreatorsMap, TitleBarStore } from './embeddedApp/titleBar'; | ||
import { toastActionCreatorsMap, ToastStore } from './embeddedApp/toast'; | ||
export { DispatchProps, StoreReadyAction, Store, AppBridgeStore, AppInfoStore, FeaturesState, LoadingStore, ModalStore, NavigationStore, ResourcePickerStore, StaffMemberStore, TitleBarStore, ToastStore, appBridgeActionCreatorsMap, appInfoActionCreatorsMap, featuresActionCreatorsMap, loadingActionCreatorsMap, modalActionCreatorsMap, navigationActionCreatorsMap, resourcePickerActionCreatorsMap, staffMemberActionCreatorsMap, titleBarActionCreatorsMap, toastActionCreatorsMap, }; | ||
export default appBridgeReducer; | ||
export declare function mapAppStoreToProps(state: Store): Store; | ||
export declare function mapAppDispatchToProps(app: Application): DispatchProps; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var appBridge_1 = __importDefault(require("./embeddedApp/appBridge")); | ||
var redux_1 = require("redux"); | ||
var appBridge_1 = __importStar(require("./embeddedApp/appBridge")); | ||
exports.appBridgeActionCreatorsMap = appBridge_1.appBridgeActionCreatorsMap; | ||
exports.StoreReadyAction = appBridge_1.StoreReadyAction; | ||
var appInfo_1 = require("./embeddedApp/appInfo"); | ||
exports.appInfoActionCreatorsMap = appInfo_1.appInfoActionCreatorsMap; | ||
var features_1 = require("./embeddedApp/features"); | ||
exports.featuresActionCreatorsMap = features_1.featuresActionCreatorsMap; | ||
var loading_1 = require("./embeddedApp/loading"); | ||
exports.loadingActionCreatorsMap = loading_1.loadingActionCreatorsMap; | ||
var modal_1 = require("./embeddedApp/modal"); | ||
exports.modalActionCreatorsMap = modal_1.modalActionCreatorsMap; | ||
var navigation_1 = require("./embeddedApp/navigation"); | ||
exports.navigationActionCreatorsMap = navigation_1.navigationActionCreatorsMap; | ||
var resourcePicker_1 = require("./embeddedApp/resourcePicker"); | ||
exports.resourcePickerActionCreatorsMap = resourcePicker_1.resourcePickerActionCreatorsMap; | ||
var staffMember_1 = require("./embeddedApp/staffMember"); | ||
exports.staffMemberActionCreatorsMap = staffMember_1.staffMemberActionCreatorsMap; | ||
var titleBar_1 = require("./embeddedApp/titleBar"); | ||
exports.titleBarActionCreatorsMap = titleBar_1.titleBarActionCreatorsMap; | ||
var toast_1 = require("./embeddedApp/toast"); | ||
exports.toastActionCreatorsMap = toast_1.toastActionCreatorsMap; | ||
exports.default = appBridge_1.default; | ||
function mapAppStoreToProps(state) { | ||
return state; | ||
} | ||
exports.mapAppStoreToProps = mapAppStoreToProps; | ||
function mapAppDispatchToProps(app) { | ||
var appDispatch = app.dispatch; | ||
return { | ||
actions: { | ||
appInfo: redux_1.bindActionCreators(appInfo_1.appInfoActionCreatorsMap, appDispatch), | ||
features: redux_1.bindActionCreators(features_1.featuresActionCreatorsMap, appDispatch), | ||
loading: redux_1.bindActionCreators(loading_1.loadingActionCreatorsMap, appDispatch), | ||
modal: redux_1.bindActionCreators(modal_1.modalActionCreatorsMap, appDispatch), | ||
navigation: redux_1.bindActionCreators(navigation_1.navigationActionCreatorsMap, appDispatch), | ||
titleBar: redux_1.bindActionCreators(titleBar_1.titleBarActionCreatorsMap, appDispatch), | ||
toast: redux_1.bindActionCreators(toast_1.toastActionCreatorsMap, appDispatch), | ||
resourcePicker: redux_1.bindActionCreators(resourcePicker_1.resourcePickerActionCreatorsMap, appDispatch), | ||
staffMember: redux_1.bindActionCreators(staffMember_1.staffMemberActionCreatorsMap, appDispatch), | ||
appBridge: redux_1.bindActionCreators(appBridge_1.appBridgeActionCreatorsMap, appDispatch), | ||
}, | ||
}; | ||
} | ||
exports.mapAppDispatchToProps = mapAppDispatchToProps; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
123216
154
2822