Skip to content

Commit

Permalink
Marketplace settings, publications, subscriptions, and routing (#2577)
Browse files Browse the repository at this point in the history
* New settings, publications, schemas for marketplace settings

* providesShopSettings panels now collapsable and display icon

* Add PrimaryShop and  MerchantShops pubs/subs. remove Shops pub/sub & SellerShops global sub

* Init marketplace flags. This enables user to checkout from the main shop with vendor shops products

* Replace temp marketplace flags with actual marketplace settings

* Use getPrimaryShopid in email config

* Add new marketplace setting marketplaceNakedRoute

* Remove default 2nd shop for marketplace

* New settings for default packages enabled by shop type

* client side getMarketplaceSettings method

* set locale conditionally for marketplace

* guard getShopPrefix on client

* guard getLogo

* conditionally get shop for brand

* Only return marketplace settings if marketplace is enabled

* Set package enabled status for new shops based on marketplace settings

* Don’t create a second primary shop

* Conditionally create the cart for the correct shop

* Flag merchantLanguage and merchantLocale for removal

* Flush currency for correct shop

* Primary shop package sub

* Cache client side marketplace settings

* Use cached marketplace settings for brand and cart shopId lookups

* rename/refactor getMarketplaceSettings on the client to match server method

* Update the templates publication to ignore shopId unless merchantTemplates is enabled

* Publish tags for both the active shop and the primary shop

* Use the primary shop for routing ReactionLayouts unless merchantTemplates is enabled

* publish the cart from the primaryShop unless merchantCarts is enabled

* stub getPrimaryShopId

* Stub getPrimaryShopId in another test

* Deprecate and replace getCurrentShop and getCurrentShopCursor

* Add getPrimaryShopId to /lib/api

* Use primaryShop to determine if revision control is enabled

* Add connectors and connectors-shopify to the default enabled packages for merchant shops
  • Loading branch information
spencern authored Jul 28, 2017
1 parent f096714 commit 1c8728f
Show file tree
Hide file tree
Showing 36 changed files with 1,289 additions and 3,820 deletions.
184 changes: 162 additions & 22 deletions client/modules/core/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,81 @@ const reactionState = new ReactiveDict();
* Global reaction shop permissions methods and shop initialization
*/
export default {
_shopId: new ReactiveVar(null),
_shopId: new ReactiveVar(null), // The active shop
_primaryShopId: new ReactiveVar(null), // The first shop created
marketplace: { _ready: false }, // Marketplace Settings

Locale: new ReactiveVar({}),

init() {
// keep an eye out for shop change
Tracker.autorun(() => {
// marketplaceSettings come over on the PrimarySHopPackages subscription
if (this.Subscriptions.PrimaryShopPackages.ready()) {
if (!this.marketplace._ready) {
const marketplacePkgSettings = this.getMarketplaceSettings();
if (marketplacePkgSettings && marketplacePkgSettings.public) {
marketplacePkgSettings._ready = true;
this.marketplace = marketplacePkgSettings.public;
}
}
}
});

// Listen for the primary shop subscription and set accordingly
Tracker.autorun(() => {
let shop;
if (this.Subscriptions.PrimaryShop.ready()) {
// if we've already set the primaryShopId, carry on.
// otherwise we need to define it.
if (!this.primaryShopId) {
// There should only ever be one "primary" shop
shop = Shops.findOne({
shopType: "primary"
});

if (shop) {
this.primaryShopId = shop._id;
this.primaryShopName = shop.name;

// We'll initialize locale and currency for the primary shop unless
// marketplace settings exist and merchantLocale is set to true
if (this.marketplace.merchantLocale !== true) {
// initialize local client Countries collection
if (!Countries.findOne()) {
createCountryCollection(shop.locales.countries);
}

const locale = this.Locale.get() || {};

// fix for https://github.com/reactioncommerce/reaction/issues/248
// we need to keep an eye for rates changes
if (typeof locale.locale === "object" &&
typeof locale.currency === "object" &&
typeof locale.locale.currency === "string") {
const localeCurrency = locale.locale.currency.split(",")[0];
if (typeof shop.currencies[localeCurrency] === "object") {
if (typeof shop.currencies[localeCurrency].rate === "number") {
locale.currency.rate = shop.currencies[localeCurrency].rate;
localeDep.changed();
}
}
}
// we are looking for a shopCurrency changes here
if (typeof locale.shopCurrency === "object") {
locale.shopCurrency = shop.currencies[shop.currency];
localeDep.changed();
}
}
}
}
}
});

// Listen for active shop change
return Tracker.autorun(() => {
let domain;
let shop;

if (this.Subscriptions.Shops.ready()) {
if (this.Subscriptions.MerchantShops.ready()) {
domain = Meteor.absoluteUrl().split("/")[2].split(":")[0];

// if we don't have an active shopId, try to retreive it from the userPreferences object
Expand All @@ -56,30 +120,35 @@ export default {
this.shopId = shop._id;
this.shopName = shop.name;
}

// We only use the active shop to setup locale if marketplace settings
// are enabled and merchantLocale is set to true
if (this.marketplace.merchantLocale === true) {
// initialize local client Countries collection
if (!Countries.findOne()) {
createCountryCollection(shop.locales.countries);
}
if (!Countries.findOne()) {
createCountryCollection(shop.locales.countries);
}

const locale = this.Locale.get() || {};
const locale = this.Locale.get() || {};

// fix for https://github.com/reactioncommerce/reaction/issues/248
// we need to keep an eye for rates changes
if (typeof locale.locale === "object" &&
// fix for https://github.com/reactioncommerce/reaction/issues/248
// we need to keep an eye for rates changes
if (typeof locale.locale === "object" &&
typeof locale.currency === "object" &&
typeof locale.locale.currency === "string") {
const localeCurrency = locale.locale.currency.split(",")[0];
if (typeof shop.currencies[localeCurrency] === "object") {
if (typeof shop.currencies[localeCurrency].rate === "number") {
locale.currency.rate = shop.currencies[localeCurrency].rate;
localeDep.changed();
const localeCurrency = locale.locale.currency.split(",")[0];
if (typeof shop.currencies[localeCurrency] === "object") {
if (typeof shop.currencies[localeCurrency].rate === "number") {
locale.currency.rate = shop.currencies[localeCurrency].rate;
localeDep.changed();
}
}
}
}
// we are looking for a shopCurrency changes here
if (typeof locale.shopCurrency === "object") {
locale.shopCurrency = shop.currencies[shop.currency];
localeDep.changed();
// we are looking for a shopCurrency changes here
if (typeof locale.shopCurrency === "object") {
locale.shopCurrency = shop.currencies[shop.currency];
localeDep.changed();
}
}
return this;
}
Expand Down Expand Up @@ -295,6 +364,58 @@ export default {
});
},

// primaryShopId is the first created shop. In a marketplace setting it's
// the shop that controls the marketplace and can see all other shops.
get primaryShopId() {
return this._primaryShopId.get();
},

set primaryShopId(shopId) {
this._primaryShopId.set(shopId);
},

getPrimaryShopId() {
return this.primaryShopId;
},

getPrimaryShopName() {
const shopId = this.getPrimaryShopId();
const shop = Shops.findOne({
_id: shopId
});

if (shop && shop.name) {
return shop.name;
}

// If we can't find the primaryShop return an empty string
return "";
},

// Primary Shop should probably not have a prefix (or should it be /shop?)
getPrimaryShopPrefix() {
return "/" + this.getSlug(this.getPrimaryShopName().toLowerCase());
},

getPrimaryShopSettings() {
const settings = Packages.findOne({
name: "core",
shopId: this.getPrimaryShopId()
}) || {};
return settings.settings || {};
},

getPrimaryShopCurrency() {
const shop = Shops.findOne({
_id: this.getPrimaryShopId()
});

return shop && shop.currency || "USD";
},

// shopId refers to the active shop. For most shoppers this will be the same
// as the primary shop, but for administrators this will usually be the shop
// they administer.
get shopId() {
return this._shopId.get();
},
Expand Down Expand Up @@ -323,7 +444,10 @@ export default {
},

getShopPrefix() {
return "/" + this.getSlug(this.getShopName().toLowerCase());
const shopName = this.getShopName();
if (shopName) {
return "/" + this.getSlug(shopName.toLowerCase());
}
},

getShopSettings() {
Expand Down Expand Up @@ -578,6 +702,22 @@ export default {
}
Logger.debug("getRegistryForCurrentRoute not found", template, provides);
return {};
},

/**
* getMarketplaceSettingsFromPackages finds the enabled `reaction-marketplace` package for
* the primary shop and returns the settings
* @method getMarketplaceSettingsFromPackages
* @return {Object} The marketplace settings from the primary shop or undefined
*/
getMarketplaceSettings() {
const marketplaceSettings = Packages.findOne({
name: "reaction-marketplace",
shopId: this.getPrimaryShopId(), // the primary shop always owns the marketplace settings
enabled: true // only use the marketplace settings if marketplace is enabled
});

return marketplaceSettings && marketplaceSettings.settings;
}

};
Expand Down
17 changes: 14 additions & 3 deletions client/modules/core/subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ Subscriptions.Account = Subscriptions.Manager.subscribe("Accounts", Meteor.userI
/**
* General Subscriptions
*/
Subscriptions.Shops = Subscriptions.Manager.subscribe("Shops");

// Primary shop subscription
Subscriptions.PrimaryShop = Subscriptions.Manager.subscribe("PrimaryShop");

// Additional shop subscriptions
Subscriptions.MerchantShops = Subscriptions.Manager.subscribe("MerchantShops");

// Init Packages sub so we have a "ready" state
Subscriptions.Packages = Subscriptions.Manager.subscribe("Packages");

Subscriptions.SellerShops = Subscriptions.Manager.subscribe("SellerShops");
Subscriptions.PrimaryShopPackages = Subscriptions.Manager.subscribe("Packages");

Subscriptions.Tags = Subscriptions.Manager.subscribe("Tags");

Expand Down Expand Up @@ -82,3 +86,10 @@ Tracker.autorun(() => {
Subscriptions.Packages = Subscriptions.Manager.subscribe("Packages", Reaction.getShopId());
}
});

Tracker.autorun(() => {
// Reload Packages sub if primaryShopId changes
if (Reaction.getPrimaryShopId()) {
Subscriptions.PrimaryShopPackages = Subscriptions.Manager.subscribe("Packages", Reaction.getPrimaryShopId());
}
});
30 changes: 27 additions & 3 deletions client/modules/i18n/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,25 @@ export const packageNamespaces = [];

Meteor.startup(() => {
Tracker.autorun(function (c) {
let merchantShopsReadyOrSkipped = false;

// Choose shopSubscription based on marketplace settings
if (Reaction.marketplaceEnabled && Reaction.merchantLanguage) {
merchantShopsReadyOrSkipped = Reaction.Subscriptions.MerchantShops.ready();
} else {
merchantShopsReadyOrSkipped = true;
}

// setting local and active packageNamespaces
// packageNamespaces are used to determine i18n namespace
if (Reaction.Subscriptions.Shops.ready()) {
if (Reaction.Subscriptions.PrimaryShop.ready() && merchantShopsReadyOrSkipped) {
const primaryShopId = Reaction.getPrimaryShopId();
// every package gets a namespace, fetch them and export
const packages = Packages.find({}, {
// get packages from primaryShopId as merchant shops
// may not have all packages
const packages = Packages.find({
shopId: primaryShopId
}, {
fields: {
name: 1
}
Expand All @@ -101,6 +115,14 @@ Meteor.startup(() => {
packageNamespaces.push(pkg.name);
}

// By default, use the primaryShopId to get locale
// If markteplace is enabled and set to use merchant currencies,
// get the active shopId
let localeShopId = primaryShopId;
if (Reaction.marketplaceEnabled && Reaction.merchantCurrency) {
localeShopId = Reaction.getShopId();
}

// use i18n detected language to getLocale info
Meteor.call("shop/getLocale", (error, result) => {
if (result) {
Expand All @@ -120,7 +142,9 @@ Meteor.startup(() => {
const primaryCurrency = locale.locale.currency.split(",")[0];
localStorage.setItem("currency", primaryCurrency);
} else {
const shop = Shops.findOne(Reaction.getShopId(), {
const shop = Shops.findOne({
_id: localeShopId
}, {
fields: {
currency: 1
}
Expand Down
21 changes: 18 additions & 3 deletions client/modules/i18n/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,21 @@ Meteor.startup(() => {
// this only runs on initial page loaded
// and when user.profile.lang updates
Tracker.autorun(function () {
if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) {
const shop = Shops.findOne(Reaction.getShopId());
if (Reaction.Subscriptions.PrimaryShop.ready() &&
Reaction.Subscriptions.MerchantShops.ready() &&
Meteor.user()) {
let shopId;

// Choose shop to get language from
if (Reaction.marketplaceEnabled && Reaction.merchantLanguage) {
shopId = Reaction.getShopId();
} else {
shopId = Reaction.getPrimaryShopId();
}

const shop = Shops.findOne({
_id: shopId
});
let language = shop.language;
if (Meteor.user() && Meteor.user().profile && Meteor.user().profile.lang) {
language = Meteor.user().profile.lang;
Expand Down Expand Up @@ -113,7 +126,9 @@ Meteor.startup(() => {
// althought it is also triggered when profile updates ( meaning .lang )
Tracker.autorun(function () {
const user = Meteor.user();
if (Reaction.Subscriptions.Shops.ready() && user) {

if (Reaction.Subscriptions.PrimaryShop.ready() &&
Reaction.Subscriptions.MerchantShops.ready() && user) {
if (user.profile && user.profile.currency) {
const localStorageCurrency = localStorage.getItem("currency");
if (localStorageCurrency !== user.profile.currency) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,20 @@ const composer = (props, onData) => {
let currentCurrency = "USD $";
const currencies = [];

if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) {
const shop = Shops.findOne(Reaction.getShopId(), {
if (Reaction.Subscriptions.PrimaryShop.ready() &&
Reaction.Subscriptions.MerchantShops.ready() && Meteor.user()) {
let shopId;

// Choose shop to get language from
if (Reaction.marketplaceEnabled && Reaction.merchantCurrency) {
shopId = Reaction.getShopId();
} else {
shopId = Reaction.getPrimaryShopId();
}

const shop = Shops.findOne({
_id: shopId
}, {
fields: {
currencies: 1,
currency: 1
Expand Down
Loading

0 comments on commit 1c8728f

Please sign in to comment.