Merge branch 'master' into master

This commit is contained in:
Baltazar Gomez 2017-08-24 12:28:05 -06:00 committed by GitHub
commit 03b04ad9b0
24 changed files with 462 additions and 62 deletions

2
.gitignore vendored
View file

@ -10,8 +10,10 @@
/app/dist
/app/locales
/app/node_modules
/build/venv
/build/daemon.ver
/lbry-app-venv
/lbry-app
/lbry-venv

View file

@ -82,9 +82,17 @@ fi
DAEMON_VER=$(node -e "console.log(require(\"$ROOT/app/package.json\").lbrySettings.lbrynetDaemonVersion)")
DAEMON_URL_TEMPLATE=$(node -e "console.log(require(\"$ROOT/app/package.json\").lbrySettings.lbrynetDaemonUrlTemplate)")
DAEMON_URL=$(echo ${DAEMON_URL_TEMPLATE//DAEMONVER/$DAEMON_VER} | sed "s/OSNAME/$OSNAME/g")
wget --quiet "$DAEMON_URL" -O "$BUILD_DIR/daemon.zip"
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
rm "$BUILD_DIR/daemon.zip"
DAEMON_VER_PATH="$BUILD_DIR/daemon.ver"
echo "$DAEMON_VER_PATH"
if [[ ! -f $DAEMON_VER_PATH || ! -f $ROOT/app/dist/lbrynet-daemon || "$(< "$DAEMON_VER_PATH")" != "$DAEMON_VER" ]]; then
wget --quiet "$DAEMON_URL" -O "$BUILD_DIR/daemon.zip"
unzip "$BUILD_DIR/daemon.zip" -d "$ROOT/app/dist/"
rm "$BUILD_DIR/daemon.zip"
echo "$DAEMON_VER" > "$DAEMON_VER_PATH"
else
echo "Already have daemon version $DAEMON_VER, skipping download"
fi

View file

@ -231,14 +231,15 @@ export function doStartDownload(uri, outpoint) {
return function(dispatch, getState) {
const state = getState();
if (!outpoint) { throw new Error("outpoint is required to begin a download"); }
if (!outpoint) {
throw new Error("outpoint is required to begin a download");
}
const { downloadingByOutpoint = {} } = state.fileInfo;
if (downloadingByOutpoint[outpoint]) return;
lbry.file_list({ outpoint, full_status: true }).then(([fileInfo]) => {
dispatch({
type: types.DOWNLOADING_STARTED,
data: {
@ -282,29 +283,32 @@ export function doLoadVideo(uri) {
},
});
lbry.get({ uri }).then(streamInfo => {
const timeout =
streamInfo === null ||
typeof streamInfo !== "object" ||
streamInfo.error == "Timeout";
lbry
.get({ uri })
.then(streamInfo => {
const timeout =
streamInfo === null ||
typeof streamInfo !== "object" ||
streamInfo.error == "Timeout";
if (timeout) {
if (timeout) {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
});
dispatch(doOpenModal("timedOut"));
} else {
dispatch(doDownloadFile(uri, streamInfo));
}
})
.catch(error => {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
});
dispatch(doOpenModal("timedOut"));
} else {
dispatch(doDownloadFile(uri, streamInfo));
}
}).catch(error => {
dispatch({
type: types.LOADING_VIDEO_FAILED,
data: { uri },
dispatch(doAlertError(error));
});
dispatch(doAlertError(error));
});
};
}

View file

@ -1,6 +1,10 @@
import * as types from "constants/action_types";
import * as settings from "constants/settings";
import batchActions from "util/batchActions";
import lbry from "lbry";
import fs from "fs";
import http from "http";
const { remote } = require("electron");
const { extname } = require("path");
@ -84,5 +88,97 @@ export function doSetTheme(name) {
// update theme
dispatch(doSetClientSetting(settings.THEME, theme.name));
}
}
export function doDownloadLanguage(langFile) {
return function(dispatch, getState) {
const destinationPath = `app/locales/${langFile}`;
const language = langFile.replace(".json", "");
const req = http.get(
{
headers: {
"Content-Type": "text/html",
},
host: "i18n.lbry.io",
path: `/langs/${langFile}`,
},
response => {
if (response.statusCode === 200) {
const file = fs.createWriteStream(destinationPath);
file.on("error", errorHandler);
file.on("finish", () => {
file.close();
// push to our local list
dispatch({
type: types.DOWNLOAD_LANGUAGE_SUCCEEDED,
data: { language: language },
});
});
response.pipe(file);
} else {
errorHandler(new Error("Language request failed."));
}
}
);
const errorHandler = err => {
fs.unlink(destinationPath, () => {}); // Delete the file async. (But we don't check the result)
dispatch({
type: types.DOWNLOAD_LANGUAGE_FAILED,
data: { language },
});
};
req.setTimeout(30000, function() {
req.abort();
});
req.on("error", errorHandler);
req.end();
};
}
export function doDownloadLanguages() {
return function(dispatch, getState) {
if (!fs.existsSync(app.i18n.directory)) {
fs.mkdirSync(app.i18n.directory);
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
try {
const files = JSON.parse(xhr.responseText);
const actions = [];
files.forEach(file => {
actions.push(doDownloadLanguage(file));
});
dispatch(batchActions(...actions));
} catch (err) {
throw err;
}
} else {
throw new Error(
__("The list of available languages could not be retrieved.")
);
}
}
};
xhr.open("get", "http://i18n.lbry.io");
xhr.send();
};
}
export function doChangeLanguage(language) {
return function(dispatch, getState) {
lbry.setClientSetting(settings.LANGUAGE, language);
app.i18n.setLocale(language);
};
}

View file

@ -62,7 +62,8 @@ class FileCard extends React.PureComponent {
? metadata.thumbnail
: null;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const isRewardContent =
claim && rewardedContentClaimIds.includes(claim.claim_id);
let description = "";
if (isResolvingUri && !claim) {
@ -95,8 +96,9 @@ class FileCard extends React.PureComponent {
<div className="card__subtitle">
<span style={{ float: "right" }}>
<FilePrice uri={uri} />
{isRewardContent && <span>{" "}<IconFeatured /></span> }
{fileInfo && <span>{" "}<Icon fixed icon="icon-folder" /></span> }
{isRewardContent && <span>{" "}<IconFeatured /></span>}
{fileInfo &&
<span>{" "}<Icon fixed icon="icon-folder" /></span>}
</span>
<UriIndicator uri={uri} />
</div>

View file

@ -71,7 +71,8 @@ class FileTile extends React.PureComponent {
? metadata.thumbnail
: null;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const isRewardContent =
claim && rewardedContentClaimIds.includes(claim.claim_id);
let onClick = () => navigate("/show", { uri });
@ -109,7 +110,7 @@ class FileTile extends React.PureComponent {
<div className="file-tile__content">
<div className="card__title-primary">
{!hidePrice ? <FilePrice uri={this.props.uri} /> : null}
{isRewardContent && <IconFeatured /> }
{isRewardContent && <IconFeatured />}
<div className="meta">{uri}</div>
<h3>
<TruncatedText lines={1}>{title}</TruncatedText>

View file

@ -3,10 +3,11 @@ import { Icon } from "component/common.js";
const IconFeatured = props => {
return (
<span className="icon-featured" title={ __("Watch content with this icon to earn weekly rewards.")}>
<Icon icon="icon-rocket"
fixed
className="card__icon-featured-content" />
<span
className="icon-featured"
title={__("Watch content with this icon to earn weekly rewards.")}
>
<Icon icon="icon-rocket" fixed className="card__icon-featured-content" />
</span>
);
};

View file

@ -21,7 +21,7 @@ const select = (state, props) => {
};
const perform = dispatch => ({
navigate: (uri) => dispatch(doNavigate(uri)),
navigate: uri => dispatch(doNavigate(uri)),
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
});

View file

@ -29,20 +29,34 @@ class UserVerify extends React.PureComponent {
<p>
{__(
"To ensure you are a real person, we require a valid credit or debit card."
) + " " + __("There is no charge at all, now or in the future.") + " " }
<Link href="https://lbry.io/faq/identity-requirements" label={__("Read More")} />
) +
" " +
__("There is no charge at all, now or in the future.") +
" "}
<Link
href="https://lbry.io/faq/identity-requirements"
label={__("Read More")}
/>
</p>
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
<p><CardVerify
label={__("Link Card and Finish")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey="pk_live_e8M4dRNnCCbmpZzduEUZBgJO"
/></p>
<p>
{__("You can continue without this step, but you will not be eligible to earn rewards.")}
<CardVerify
label={__("Link Card and Finish")}
disabled={isPending}
token={this.onToken.bind(this)}
stripeKey="pk_live_e8M4dRNnCCbmpZzduEUZBgJO"
/>
</p>
<Link onClick={() => navigate("/discover")} button="alt" label={__("Skip Rewards")} />
<p>
{__(
"You can continue without this step, but you will not be eligible to earn rewards."
)}
</p>
<Link
onClick={() => navigate("/discover")}
button="alt"
label={__("Skip Rewards")}
/>
</div>
);
}

View file

@ -16,7 +16,11 @@ class Video extends React.PureComponent {
componentWillReceiveProps(nextProps) {
// reset playing state upon change path action
if (!this.isMediaSame(nextProps) && this.props.fileInfo && this.state.isPlaying) {
if (
!this.isMediaSame(nextProps) &&
this.props.fileInfo &&
this.state.isPlaying
) {
this.state.isPlaying = false;
}
}

View file

@ -113,3 +113,7 @@ export const CLAIM_REWARD_SUCCESS = "CLAIM_REWARD_SUCCESS";
export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
//Language
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";

View file

@ -0,0 +1,187 @@
const LANGUAGES = {
aa: ["Afar", "Afar"],
ab: ["Abkhazian", "Аҧсуа"],
af: ["Afrikaans", "Afrikaans"],
ak: ["Akan", "Akana"],
am: ["Amharic", "አማርኛ"],
an: ["Aragonese", "Aragonés"],
ar: ["Arabic", "العربية"],
as: ["Assamese", "অসমীয়া"],
av: ["Avar", "Авар"],
ay: ["Aymara", "Aymar"],
az: ["Azerbaijani", "Azərbaycanca / آذربايجان"],
ba: ["Bashkir", "Башҡорт"],
be: ["Belarusian", "Беларуская"],
bg: ["Bulgarian", "Български"],
bh: ["Bihari", "भोजपुरी"],
bi: ["Bislama", "Bislama"],
bm: ["Bambara", "Bamanankan"],
bn: ["Bengali", "বাংলা"],
bo: ["Tibetan", "བོད་ཡིག / Bod skad"],
br: ["Breton", "Brezhoneg"],
bs: ["Bosnian", "Bosanski"],
ca: ["Catalan", "Català"],
ce: ["Chechen", "Нохчийн"],
ch: ["Chamorro", "Chamoru"],
co: ["Corsican", "Corsu"],
cr: ["Cree", "Nehiyaw"],
cs: ["Czech", "Česky"],
cu: ["Old Church Slavonic / Old Bulgarian", "словѣньскъ / slověnĭskŭ"],
cv: ["Chuvash", "Чăваш"],
cy: ["Welsh", "Cymraeg"],
da: ["Danish", "Dansk"],
de: ["German", "Deutsch"],
dv: ["Divehi", "ދިވެހިބަސް"],
dz: ["Dzongkha", "ཇོང་ཁ"],
ee: ["Ewe", "Ɛʋɛ"],
el: ["Greek", "Ελληνικά"],
en: ["English", "English"],
eo: ["Esperanto", "Esperanto"],
es: ["Spanish", "Español"],
et: ["Estonian", "Eesti"],
eu: ["Basque", "Euskara"],
fa: ["Persian", "فارسی"],
ff: ["Peul", "Fulfulde"],
fi: ["Finnish", "Suomi"],
fj: ["Fijian", "Na Vosa Vakaviti"],
fo: ["Faroese", "Føroyskt"],
fr: ["French", "Français"],
fy: ["West Frisian", "Frysk"],
ga: ["Irish", "Gaeilge"],
gd: ["Scottish Gaelic", "Gàidhlig"],
gl: ["Galician", "Galego"],
gn: ["Guarani", "Avañe'ẽ"],
gu: ["Gujarati", "ગુજરાતી"],
gv: ["Manx", "Gaelg"],
ha: ["Hausa", "هَوُسَ"],
he: ["Hebrew", "עברית"],
hi: ["Hindi", "हिन्दी"],
ho: ["Hiri Motu", "Hiri Motu"],
hr: ["Croatian", "Hrvatski"],
ht: ["Haitian", "Krèyol ayisyen"],
hu: ["Hungarian", "Magyar"],
hy: ["Armenian", "Հայերեն"],
hz: ["Herero", "Otsiherero"],
ia: ["Interlingua", "Interlingua"],
id: ["Indonesian", "Bahasa Indonesia"],
ie: ["Interlingue", "Interlingue"],
ig: ["Igbo", "Igbo"],
ii: ["Sichuan Yi", "ꆇꉙ / 四川彝语"],
ik: ["Inupiak", "Iñupiak"],
io: ["Ido", "Ido"],
is: ["Icelandic", "Íslenska"],
it: ["Italian", "Italiano"],
iu: ["Inuktitut", "ᐃᓄᒃᑎᑐᑦ"],
ja: ["Japanese", "日本語"],
jv: ["Javanese", "Basa Jawa"],
ka: ["Georgian", "ქართული"],
kg: ["Kongo", "KiKongo"],
ki: ["Kikuyu", "Gĩkũyũ"],
kj: ["Kuanyama", "Kuanyama"],
kk: ["Kazakh", "Қазақша"],
kl: ["Greenlandic", "Kalaallisut"],
km: ["Cambodian", "ភាសាខ្មែរ"],
kn: ["Kannada", "ಕನ್ನಡ"],
ko: ["Korean", "한국어"],
kr: ["Kanuri", "Kanuri"],
ks: ["Kashmiri", "कश्मीरी / كشميري"],
ku: ["Kurdish", "Kurdî / كوردی"],
kv: ["Komi", "Коми"],
kw: ["Cornish", "Kernewek"],
ky: ["Kirghiz", "Kırgızca / Кыргызча"],
la: ["Latin", "Latina"],
lb: ["Luxembourgish", "Lëtzebuergesch"],
lg: ["Ganda", "Luganda"],
li: ["Limburgian", "Limburgs"],
ln: ["Lingala", "Lingála"],
lo: ["Laotian", "ລາວ / Pha xa lao"],
lt: ["Lithuanian", "Lietuvių"],
lv: ["Latvian", "Latviešu"],
mg: ["Malagasy", "Malagasy"],
mh: ["Marshallese", "Kajin Majel / Ebon"],
mi: ["Maori", "Māori"],
mk: ["Macedonian", "Македонски"],
ml: ["Malayalam", "മലയാളം"],
mn: ["Mongolian", "Монгол"],
mo: ["Moldovan", "Moldovenească"],
mr: ["Marathi", "मराठी"],
ms: ["Malay", "Bahasa Melayu"],
mt: ["Maltese", "bil-Malti"],
my: ["Burmese", "Myanmasa"],
na: ["Nauruan", "Dorerin Naoero"],
nd: ["North Ndebele", "Sindebele"],
ne: ["Nepali", "नेपाली"],
ng: ["Ndonga", "Oshiwambo"],
nl: ["Dutch", "Nederlands"],
nn: ["Norwegian Nynorsk", "Norsk (nynorsk)"],
no: ["Norwegian", "Norsk (bokmål / riksmål)"],
nr: ["South Ndebele", "isiNdebele"],
nv: ["Navajo", "Diné bizaad"],
ny: ["Chichewa", "Chi-Chewa"],
oc: ["Occitan", "Occitan"],
oj: ["Ojibwa", "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin"],
om: ["Oromo", "Oromoo"],
or: ["Oriya", "ଓଡ଼ିଆ"],
os: ["Ossetian / Ossetic", "Иронау"],
pa: ["Panjabi / Punjabi", "ਪੰਜਾਬੀ / पंजाबी / پنجابي"],
pi: ["Pali", "Pāli / पाऴि"],
pl: ["Polish", "Polski"],
ps: ["Pashto", "پښتو"],
pt: ["Portuguese", "Português"],
qu: ["Quechua", "Runa Simi"],
rm: ["Raeto Romance", "Rumantsch"],
rn: ["Kirundi", "Kirundi"],
ro: ["Romanian", "Română"],
ru: ["Russian", "Русский"],
rw: ["Rwandi", "Kinyarwandi"],
sa: ["Sanskrit", "संस्कृतम्"],
sc: ["Sardinian", "Sardu"],
sd: ["Sindhi", "सिनधि"],
se: ["Northern Sami", "Sámegiella"],
sg: ["Sango", "Sängö"],
sh: ["Serbo-Croatian", "Srpskohrvatski / Српскохрватски"],
si: ["Sinhalese", "සිංහල"],
sk: ["Slovak", "Slovenčina"],
sl: ["Slovenian", "Slovenščina"],
sm: ["Samoan", "Gagana Samoa"],
sn: ["Shona", "chiShona"],
so: ["Somalia", "Soomaaliga"],
sq: ["Albanian", "Shqip"],
sr: ["Serbian", "Српски"],
ss: ["Swati", "SiSwati"],
st: ["Southern Sotho", "Sesotho"],
su: ["Sundanese", "Basa Sunda"],
sv: ["Swedish", "Svenska"],
sw: ["Swahili", "Kiswahili"],
ta: ["Tamil", "தமிழ்"],
te: ["Telugu", "తెలుగు"],
tg: ["Tajik", "Тоҷикӣ"],
th: ["Thai", "ไทย / Phasa Thai"],
ti: ["Tigrinya", "ትግርኛ"],
tk: ["Turkmen", "Туркмен / تركمن"],
tl: ["Tagalog / Filipino", "Tagalog"],
tn: ["Tswana", "Setswana"],
to: ["Tonga", "Lea Faka-Tonga"],
tr: ["Turkish", "Türkçe"],
ts: ["Tsonga", "Xitsonga"],
tt: ["Tatar", "Tatarça"],
tw: ["Twi", "Twi"],
ty: ["Tahitian", "Reo Mā`ohi"],
ug: ["Uyghur", "Uyƣurqə / ئۇيغۇرچە"],
uk: ["Ukrainian", "Українська"],
ur: ["Urdu", "اردو"],
uz: ["Uzbek", "Ўзбек"],
ve: ["Venda", "Tshivenḓa"],
vi: ["Vietnamese", "Tiếng Việt"],
vo: ["Volapük", "Volapük"],
wa: ["Walloon", "Walon"],
wo: ["Wolof", "Wollof"],
xh: ["Xhosa", "isiXhosa"],
yi: ["Yiddish", "ייִדיש"],
yo: ["Yoruba", "Yorùbá"],
za: ["Zhuang", "Cuengh / Tôô / 壮语"],
zh: ["Chinese", "中文"],
zu: ["Zulu", "isiZulu"],
};
export default LANGUAGES;

View file

@ -27,7 +27,7 @@ jsonrpc.call = function(
xhr.addEventListener("load", function() {
var response = JSON.parse(xhr.responseText);
let error = response.error || response.result && response.result.error;
let error = response.error || (response.result && response.result.error);
if (error) {
if (errorCallback) {
errorCallback(error);

View file

@ -7,6 +7,7 @@ import { Provider } from "react-redux";
import store from "store.js";
import SplashScreen from "component/splash";
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
import { doDownloadLanguages } from "actions/settings";
import { toQueryString } from "util/query_params";
import * as types from "constants/action_types";
@ -96,6 +97,8 @@ const updateProgress = () => {
const initialState = app.store.getState();
var init = function() {
app.store.dispatch(doDownloadLanguages());
function onDaemonReady() {
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
app.store.dispatch(doDaemonReady());

View file

@ -20,17 +20,22 @@ class BackupPage extends React.PureComponent {
<SubHeader />
<section className="card">
<div className="card__title-primary">
<h3>{__("Backup Wallet")}</h3>
<h3>{__("Backup Your LBRY Credits")}</h3>
</div>
<div className="card__content">
<p>
{__(
"Currently, there is no automatic wallet backup, but it is fairly easy to back up manually."
"Your LBRY credits are controllable by you and only you, via wallet file(s) stored locally on your computer."
)}
</p>
<p>
{__(
"To backup your wallet, make a copy of the folder listed below:"
"Currently, there is no automatic wallet backup. If you lose access to these files, you will lose your credits permanently."
)}
</p>
<p>
{__(
"However, it is fairly easy to back up manually. To backup your wallet, make a copy of the folder listed below:"
)}
</p>
<p>
@ -45,6 +50,13 @@ class BackupPage extends React.PureComponent {
)}
</strong>
</p>
<p>
For more details on backing up and best practices,{" "}
<Link
href="https://lbry.io/faq/how-to-backup-wallet"
label={__("see this article")}
/>.
</p>
</div>
</section>
</main>

View file

@ -111,7 +111,7 @@ class FilePage extends React.PureComponent {
{!fileInfo || fileInfo.written_bytes <= 0
? <span style={{ float: "right" }}>
<FilePrice uri={lbryuri.normalize(uri)} />
{isRewardContent && <span>{" "}<IconFeatured /></span> }
{isRewardContent && <span>{" "}<IconFeatured /></span>}
</span>
: null}
<h1>{title}</h1>

View file

@ -127,7 +127,9 @@ class RewardsPage extends React.PureComponent {
<div>
<div className="card__content empty">
<p>
{__("This application is unable to earn rewards due to an authentication failure.")}
{__(
"This application is unable to earn rewards due to an authentication failure."
)}
</p>
</div>
</div>

View file

@ -6,13 +6,21 @@ import {
doSetClientSetting,
doGetThemes,
doSetTheme,
doChangeLanguage,
} from "actions/settings";
import { selectDaemonSettings, selectShowNsfw } from "selectors/settings";
import {
selectDaemonSettings,
selectShowNsfw,
selectLanguages,
} from "selectors/settings";
import { selectCurrentLanguage } from "selectors/app";
import SettingsPage from "./view";
const select = state => ({
daemonSettings: selectDaemonSettings(state),
showNsfw: selectShowNsfw(state),
language: selectCurrentLanguage(state),
languages: selectLanguages(state),
});
const perform = dispatch => ({
@ -21,6 +29,7 @@ const perform = dispatch => ({
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setTheme: name => dispatch(doSetTheme(name)),
getThemes: () => dispatch(doGetThemes),
changeLanguage: newLanguage => dispatch(doChangeLanguage(newLanguage)),
});
export default connect(select, perform)(SettingsPage);

View file

@ -110,11 +110,10 @@ class SettingsPage extends React.PureComponent {
this.props.setClientSetting(settings.SHOW_NSFW, event.target.checked);
}
// onLanguageChange(language) {
// lbry.setClientSetting('language', language);
// i18n.setLocale(language);
// this.setState({language: language})
// }
onLanguageChange(e) {
this.props.changeLanguage(e.target.value);
this.forceUpdate();
}
onShowUnavailableChange(event) {}
@ -125,7 +124,7 @@ class SettingsPage extends React.PureComponent {
componentDidMount() {}
render() {
const { daemonSettings } = this.props;
const { daemonSettings, language, languages } = this.props;
if (!daemonSettings || Object.keys(daemonSettings).length === 0) {
return (
@ -137,6 +136,28 @@ class SettingsPage extends React.PureComponent {
return (
<main className="main--single-column">
<SubHeader />
<section className="card">
<div className="card__content">
<h3>{__("Language")}</h3>
</div>
<div className="card__content">
<div className="form-row">
<FormField
type="select"
name="language"
defaultValue={language}
onChange={this.onLanguageChange.bind(this)}
>
<option value="en">{__("English")}</option>
{Object.keys(languages).map(dLang =>
<option key={dLang} value={dLang}>
{languages[dLang]}
</option>
)}
</FormField>
</div>
</div>
</section>
<section className="card">
<div className="card__content">
<h3>{__("Download Directory")}</h3>

View file

@ -21,10 +21,7 @@ reducers[types.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) {
});
};
reducers[types.FETCH_REWARD_CONTENT_COMPLETED] = function(
state,
action
) {
reducers[types.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
const { claimIds, success } = action.data;
return Object.assign({}, state, {

View file

@ -1,5 +1,6 @@
import * as types from "constants/action_types";
import * as settings from "constants/settings";
import LANGUAGES from "constants/languages";
import lbry from "lbry";
const reducers = {};
@ -8,7 +9,9 @@ const defaultState = {
showNsfw: lbry.getClientSetting("showNsfw"),
theme: lbry.getClientSetting(settings.THEME),
themes: lbry.getClientSetting(settings.THEMES),
language: lbry.getClientSetting("language"),
},
languages: {},
};
reducers[types.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
@ -28,6 +31,28 @@ reducers[types.CLIENT_SETTING_CHANGED] = function(state, action) {
});
};
reducers[types.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) {
const languages = Object.assign({}, state.languages);
const language = action.data.language;
const langCode = language.substring(0, 2);
if (LANGUAGES[langCode]) {
languages[language] =
LANGUAGES[langCode][0] + " (" + LANGUAGES[langCode][1] + ")";
} else {
languages[langCode] = langCode;
}
return Object.assign({}, state, { languages });
};
reducers[types.DOWNLOAD_LANGUAGE_FAILED] = function(state, action) {
const languages = Object.assign({}, state.languages);
delete languages[action.data.language];
return Object.assign({}, state, { languages });
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

View file

@ -218,6 +218,11 @@ export const selectBadgeNumber = createSelector(
state => state.badgeNumber
);
export const selectCurrentLanguage = createSelector(
_selectState,
() => app.i18n.getLocale() || "en"
);
export const selectPathAfterAuth = createSelector(
_selectState,
state => state.pathAfterAuth

View file

@ -21,3 +21,8 @@ export const selectShowNsfw = createSelector(
selectClientSettings,
clientSettings => !!clientSettings.showNsfw
);
export const selectLanguages = createSelector(
_selectState,
state => state.languages || {}
);

View file

@ -1,5 +1,3 @@
const { remote } = require("electron");
/**
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
* is not set yet.