mirror of https://github.com/abpframework/abp.git
137 changed files with 14725 additions and 261 deletions
@ -0,0 +1,341 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Dynamic.Core; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.Linq; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.ObjectMapping; |
||||
|
|
||||
|
namespace Volo.Abp.Application.Services |
||||
|
{ |
||||
|
public abstract class AbstractKeyCrudAppService<TEntity, TEntityDto, TKey> |
||||
|
: AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto> |
||||
|
where TEntity : class, IEntity |
||||
|
{ |
||||
|
protected AbstractKeyCrudAppService(IRepository<TEntity> repository) |
||||
|
: base(repository) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract class AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, TGetListInput> |
||||
|
: AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto, TEntityDto> |
||||
|
where TEntity : class, IEntity |
||||
|
{ |
||||
|
protected AbstractKeyCrudAppService(IRepository<TEntity> repository) |
||||
|
: base(repository) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract class AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput> |
||||
|
: AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> |
||||
|
where TEntity : class, IEntity |
||||
|
{ |
||||
|
protected AbstractKeyCrudAppService(IRepository<TEntity> repository) |
||||
|
: base(repository) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract class AbstractKeyCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
||||
|
: AbstractKeyCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
||||
|
where TEntity : class, IEntity |
||||
|
{ |
||||
|
protected AbstractKeyCrudAppService(IRepository<TEntity> repository) |
||||
|
: base(repository) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected override TEntityDto MapToGetListOutputDto(TEntity entity) |
||||
|
{ |
||||
|
return MapToGetOutputDto(entity); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract class AbstractKeyCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
||||
|
: ApplicationService, |
||||
|
ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
||||
|
where TEntity : class, IEntity |
||||
|
{ |
||||
|
public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } |
||||
|
|
||||
|
protected IRepository<TEntity> Repository { get; } |
||||
|
|
||||
|
protected virtual string GetPolicyName { get; set; } |
||||
|
|
||||
|
protected virtual string GetListPolicyName { get; set; } |
||||
|
|
||||
|
protected virtual string CreatePolicyName { get; set; } |
||||
|
|
||||
|
protected virtual string UpdatePolicyName { get; set; } |
||||
|
|
||||
|
protected virtual string DeletePolicyName { get; set; } |
||||
|
|
||||
|
protected AbstractKeyCrudAppService(IRepository<TEntity> repository) |
||||
|
{ |
||||
|
Repository = repository; |
||||
|
AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<TGetOutputDto> GetAsync(TKey id) |
||||
|
{ |
||||
|
await CheckGetPolicyAsync(); |
||||
|
|
||||
|
var entity = await GetEntityByIdAsync(id); |
||||
|
return MapToGetOutputDto(entity); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input) |
||||
|
{ |
||||
|
await CheckGetListPolicyAsync(); |
||||
|
|
||||
|
var query = CreateFilteredQuery(input); |
||||
|
|
||||
|
var totalCount = await AsyncQueryableExecuter.CountAsync(query); |
||||
|
|
||||
|
query = ApplySorting(query, input); |
||||
|
query = ApplyPaging(query, input); |
||||
|
|
||||
|
var entities = await AsyncQueryableExecuter.ToListAsync(query); |
||||
|
|
||||
|
return new PagedResultDto<TGetListOutputDto>( |
||||
|
totalCount, |
||||
|
entities.Select(MapToGetListOutputDto).ToList() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input) |
||||
|
{ |
||||
|
await CheckCreatePolicyAsync(); |
||||
|
|
||||
|
var entity = MapToEntity(input); |
||||
|
|
||||
|
TryToSetTenantId(entity); |
||||
|
|
||||
|
await Repository.InsertAsync(entity, autoSave: true); |
||||
|
|
||||
|
return MapToGetOutputDto(entity); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input) |
||||
|
{ |
||||
|
await CheckUpdatePolicyAsync(); |
||||
|
|
||||
|
var entity = await GetEntityByIdAsync(id); |
||||
|
//TODO: Check if input has id different than given id and normalize if it's default value, throw ex otherwise
|
||||
|
MapToEntity(input, entity); |
||||
|
await Repository.UpdateAsync(entity, autoSave: true); |
||||
|
|
||||
|
return MapToGetOutputDto(entity); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task DeleteAsync(TKey id) |
||||
|
{ |
||||
|
await CheckDeletePolicyAsync(); |
||||
|
|
||||
|
await DeleteByIdAsync(id); |
||||
|
} |
||||
|
|
||||
|
protected abstract Task DeleteByIdAsync(TKey id); |
||||
|
|
||||
|
protected abstract Task<TEntity> GetEntityByIdAsync(TKey id); |
||||
|
|
||||
|
protected virtual async Task CheckGetPolicyAsync() |
||||
|
{ |
||||
|
await CheckPolicyAsync(GetPolicyName); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CheckGetListPolicyAsync() |
||||
|
{ |
||||
|
await CheckPolicyAsync(GetListPolicyName); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CheckCreatePolicyAsync() |
||||
|
{ |
||||
|
await CheckPolicyAsync(CreatePolicyName); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CheckUpdatePolicyAsync() |
||||
|
{ |
||||
|
await CheckPolicyAsync(UpdatePolicyName); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CheckDeletePolicyAsync() |
||||
|
{ |
||||
|
await CheckPolicyAsync(DeletePolicyName); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Should apply sorting if needed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="query">The query.</param>
|
||||
|
/// <param name="input">The input.</param>
|
||||
|
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetListInput input) |
||||
|
{ |
||||
|
//Try to sort query if available
|
||||
|
if (input is ISortedResultRequest sortInput) |
||||
|
{ |
||||
|
if (!sortInput.Sorting.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
return query.OrderBy(sortInput.Sorting); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//IQueryable.Task requires sorting, so we should sort if Take will be used.
|
||||
|
if (input is ILimitedResultRequest) |
||||
|
{ |
||||
|
return ApplyDefaultSorting(query); |
||||
|
} |
||||
|
|
||||
|
//No sorting
|
||||
|
return query; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies sorting if no sorting specified but a limited result requested.
|
||||
|
/// </summary>
|
||||
|
/// <param name="query">The query.</param>
|
||||
|
protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query) |
||||
|
{ |
||||
|
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>()) |
||||
|
{ |
||||
|
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime); |
||||
|
} |
||||
|
|
||||
|
throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyCrudAppService!"); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Should apply paging if needed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="query">The query.</param>
|
||||
|
/// <param name="input">The input.</param>
|
||||
|
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetListInput input) |
||||
|
{ |
||||
|
//Try to use paging if available
|
||||
|
if (input is IPagedResultRequest pagedInput) |
||||
|
{ |
||||
|
return query.PageBy(pagedInput); |
||||
|
} |
||||
|
|
||||
|
//Try to limit query result if available
|
||||
|
if (input is ILimitedResultRequest limitedInput) |
||||
|
{ |
||||
|
return query.Take(limitedInput.MaxResultCount); |
||||
|
} |
||||
|
|
||||
|
//No paging
|
||||
|
return query; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method should create <see cref="IQueryable{TEntity}"/> based on given input.
|
||||
|
/// It should filter query if needed, but should not do sorting or paging.
|
||||
|
/// Sorting should be done in <see cref="ApplySorting"/> and paging should be done in <see cref="ApplyPaging"/>
|
||||
|
/// methods.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The input.</param>
|
||||
|
protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input) |
||||
|
{ |
||||
|
return Repository; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
|
||||
|
/// It uses <see cref="IObjectMapper"/> by default.
|
||||
|
/// It can be overriden for custom mapping.
|
||||
|
/// </summary>
|
||||
|
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity) |
||||
|
{ |
||||
|
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
|
||||
|
/// It uses <see cref="IObjectMapper"/> by default.
|
||||
|
/// It can be overriden for custom mapping.
|
||||
|
/// </summary>
|
||||
|
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity) |
||||
|
{ |
||||
|
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
|
||||
|
/// It uses <see cref="IObjectMapper"/> by default.
|
||||
|
/// It can be overriden for custom mapping.
|
||||
|
/// </summary>
|
||||
|
protected virtual TEntity MapToEntity(TCreateInput createInput) |
||||
|
{ |
||||
|
var entity = ObjectMapper.Map<TCreateInput, TEntity>(createInput); |
||||
|
SetIdForGuids(entity); |
||||
|
return entity; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets Id value for the entity if <see cref="TKey"/> is <see cref="Guid"/>.
|
||||
|
/// It's used while creating a new entity.
|
||||
|
/// </summary>
|
||||
|
protected virtual void SetIdForGuids(TEntity entity) |
||||
|
{ |
||||
|
var entityWithGuidId = entity as IEntity<Guid>; |
||||
|
|
||||
|
if (entityWithGuidId == null || entityWithGuidId.Id != Guid.Empty) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
EntityHelper.TrySetId( |
||||
|
entityWithGuidId, |
||||
|
() => GuidGenerator.Create(), |
||||
|
true |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Maps <see cref="TUpdateInput"/> to <see cref="TEntity"/> to update the entity.
|
||||
|
/// It uses <see cref="IObjectMapper"/> by default.
|
||||
|
/// It can be overriden for custom mapping.
|
||||
|
/// </summary>
|
||||
|
protected virtual void MapToEntity(TUpdateInput updateInput, TEntity entity) |
||||
|
{ |
||||
|
ObjectMapper.Map(updateInput, entity); |
||||
|
} |
||||
|
|
||||
|
protected virtual void TryToSetTenantId(TEntity entity) |
||||
|
{ |
||||
|
if (entity is IMultiTenant && HasTenantIdProperty(entity)) |
||||
|
{ |
||||
|
var tenantId = CurrentTenant.Id; |
||||
|
|
||||
|
if (!tenantId.HasValue) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var propertyInfo = entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)); |
||||
|
|
||||
|
if (propertyInfo == null || propertyInfo.GetSetMethod(true) == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
propertyInfo.SetValue(entity, tenantId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool HasTenantIdProperty(TEntity entity) |
||||
|
{ |
||||
|
return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.TestApp.Domain; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.TestApp.Application.Dto; |
||||
|
|
||||
|
namespace Volo.Abp.TestApp.Application |
||||
|
{ |
||||
|
//This is especially used to test the AbstractKeyCrudAppService
|
||||
|
public class DistrictAppService : AbstractKeyCrudAppService<District, DistrictDto, DistrictKey> |
||||
|
{ |
||||
|
public DistrictAppService(IRepository<District> repository) |
||||
|
: base(repository) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override async Task DeleteByIdAsync(DistrictKey id) |
||||
|
{ |
||||
|
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name); |
||||
|
} |
||||
|
|
||||
|
protected override async Task<District> GetEntityByIdAsync(DistrictKey id) |
||||
|
{ |
||||
|
return await AsyncQueryableExecuter.FirstOrDefaultAsync( |
||||
|
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.TestApp.Application |
||||
|
{ |
||||
|
public class DistrictKey |
||||
|
{ |
||||
|
public Guid CityId { get; set; } |
||||
|
|
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace Volo.Abp.TestApp.Application.Dto |
||||
|
{ |
||||
|
public class DistrictDto : EntityDto |
||||
|
{ |
||||
|
public Guid CityId { get; set; } |
||||
|
|
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public int Population { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
{ |
||||
|
"extends": ["airbnb", "prettier", "prettier/react"], |
||||
|
"parser": "babel-eslint", |
||||
|
"env": { |
||||
|
"jest": true |
||||
|
}, |
||||
|
"rules": { |
||||
|
"no-use-before-define": 0, |
||||
|
"react/jsx-filename-extension": 0, |
||||
|
"react/prop-types": ["error", { "ignore": ["navigation", "children"] }], |
||||
|
"react/require-default-props": 0, |
||||
|
"react/jsx-props-no-spreading": 0, |
||||
|
"react/forbid-prop-types": 0, |
||||
|
"import/prefer-default-export": 0, |
||||
|
"comma-dangle": 0, |
||||
|
"no-underscore-dangle": 1, |
||||
|
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], |
||||
|
"no-param-reassign": 0, |
||||
|
"operator-linebreak": 0, |
||||
|
"global-require": 0 |
||||
|
}, |
||||
|
"globals": { |
||||
|
"fetch": false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
node_modules/**/* |
||||
|
.expo/* |
||||
|
npm-debug.* |
||||
|
*.jks |
||||
|
*.p8 |
||||
|
*.p12 |
||||
|
*.key |
||||
|
*.mobileprovision |
||||
|
*.orig.* |
||||
|
web-build/ |
||||
|
web-report/ |
||||
|
|
||||
|
# macOS |
||||
|
.DS_Store |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"trailingComma": "all", |
||||
|
"singleQuote": true, |
||||
|
"jsxSingleQuote": false, |
||||
|
"printWidth": 100, |
||||
|
"semi": true, |
||||
|
"jsxBracketSameLine": true |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
import { StyleProvider } from 'native-base'; |
||||
|
import React from 'react'; |
||||
|
import { enableScreens } from 'react-native-screens'; |
||||
|
import { Provider } from 'react-redux'; |
||||
|
import { PersistGate } from 'redux-persist/integration/react'; |
||||
|
import AppContainer from './src/components/AppContainer/AppContainer'; |
||||
|
import { store, persistor } from './src/store'; |
||||
|
import getTheme from './src/theme/components'; |
||||
|
import { activeTheme } from './src/theme/variables'; |
||||
|
import { initAPIInterceptor } from './src/interceptors/APIInterceptor'; |
||||
|
|
||||
|
enableScreens(); |
||||
|
initAPIInterceptor(store); |
||||
|
|
||||
|
export default function App() { |
||||
|
return ( |
||||
|
<Provider store={store}> |
||||
|
<PersistGate loading={null} persistor={persistor}> |
||||
|
<StyleProvider style={getTheme(activeTheme)}> |
||||
|
<AppContainer /> |
||||
|
</StyleProvider> |
||||
|
</PersistGate> |
||||
|
</Provider> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
const ENV = { |
||||
|
dev: { |
||||
|
apiUrl: 'http://localhost:44305', |
||||
|
oAuthConfig: { |
||||
|
issuer: 'http://localhost:44305', |
||||
|
clientId: 'MyProjectName_App', |
||||
|
clientSecret: '1q2w3e*', |
||||
|
scope: 'MyProjectName', |
||||
|
}, |
||||
|
localization: { |
||||
|
defaultResourceName: 'MyProjectName', |
||||
|
}, |
||||
|
}, |
||||
|
prod: { |
||||
|
apiUrl: 'http://localhost:44305', |
||||
|
oAuthConfig: { |
||||
|
issuer: 'http://localhost:44305', |
||||
|
clientId: 'MyProjectName_App', |
||||
|
clientSecret: '1q2w3e*', |
||||
|
scope: 'MyProjectName', |
||||
|
}, |
||||
|
localization: { |
||||
|
defaultResourceName: 'MyProjectName', |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export const getEnvVars = () => { |
||||
|
// eslint-disable-next-line no-undef
|
||||
|
return __DEV__ ? ENV.dev : ENV.prod; |
||||
|
}; |
||||
@ -0,0 +1,30 @@ |
|||||
|
{ |
||||
|
"expo": { |
||||
|
"name": "MyProjectName", |
||||
|
"slug": "MyProjectName", |
||||
|
"privacy": "public", |
||||
|
"sdkVersion": "36.0.0", |
||||
|
"platforms": ["ios", "android", "web"], |
||||
|
"version": "1.0.0", |
||||
|
"orientation": "portrait", |
||||
|
"icon": "./assets/icon.png", |
||||
|
"splash": { |
||||
|
"image": "./assets/splash.png", |
||||
|
"resizeMode": "cover", |
||||
|
"backgroundColor": "#38003c" |
||||
|
}, |
||||
|
"updates": { |
||||
|
"fallbackToCacheTimeout": 0 |
||||
|
}, |
||||
|
"assetBundlePatterns": ["**/*"], |
||||
|
"ios": { |
||||
|
"supportsTablet": true, |
||||
|
"bundleIdentifier": "com.MyCompanyName.MyProjectName", |
||||
|
"buildNumber": "1.0.0" |
||||
|
}, |
||||
|
"android": { |
||||
|
"package": "com.MyCompanyName.MyProjectName", |
||||
|
"versionCode": 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 93 KiB |
@ -0,0 +1,6 @@ |
|||||
|
module.exports = function(api) { |
||||
|
api.cache(true); |
||||
|
return { |
||||
|
presets: ['babel-preset-expo'], |
||||
|
}; |
||||
|
}; |
||||
@ -0,0 +1,62 @@ |
|||||
|
{ |
||||
|
"main": "node_modules/expo/AppEntry.js", |
||||
|
"scripts": { |
||||
|
"start": "expo start", |
||||
|
"android": "expo start --android", |
||||
|
"ios": "expo start --ios", |
||||
|
"web": "expo start --web", |
||||
|
"eject": "expo eject", |
||||
|
"lint": "eslint *.js **/*.js", |
||||
|
"lint:fix": "yarn lint --fix" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@expo/vector-icons": "^10.0.6", |
||||
|
"@react-native-community/masked-view": "0.1.5", |
||||
|
"@react-navigation/drawer": "^5.1.1", |
||||
|
"@react-navigation/native": "^5.0.9", |
||||
|
"@react-navigation/stack": "^5.1.1", |
||||
|
"@reduxjs/toolkit": "^1.2.3", |
||||
|
"axios": "^0.19.2", |
||||
|
"color": "^3.1.2", |
||||
|
"expo": "~36.0.0", |
||||
|
"expo-constants": "~8.0.0", |
||||
|
"expo-font": "~8.0.0", |
||||
|
"formik": "^2.1.2", |
||||
|
"i18n-js": "^3.5.1", |
||||
|
"lodash": "^4.17.15", |
||||
|
"native-base": "^2.13.8", |
||||
|
"prop-types": "^15.7.2", |
||||
|
"react": "~16.9.0", |
||||
|
"react-dom": "~16.9.0", |
||||
|
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz", |
||||
|
"react-native-gesture-handler": "~1.5.0", |
||||
|
"react-native-reanimated": "~1.4.0", |
||||
|
"react-native-safe-area-context": "0.6.0", |
||||
|
"react-native-safe-area-view": "^1.0.0", |
||||
|
"react-native-screens": "2.0.0-alpha.12", |
||||
|
"react-native-web": "~0.11.7", |
||||
|
"react-redux": "^7.1.3", |
||||
|
"redux-persist": "^6.0.0", |
||||
|
"redux-saga": "^1.1.3", |
||||
|
"reselect": "^4.0.0", |
||||
|
"yup": "^0.28.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@babel/core": "^7.0.0", |
||||
|
"@types/i18n-js": "^3.0.1", |
||||
|
"@types/react": "~16.9.0", |
||||
|
"@types/react-native": "~0.60.23", |
||||
|
"@types/react-redux": "^7.1.7", |
||||
|
"@types/yup": "^0.26.29", |
||||
|
"babel-eslint": "^10.0.3", |
||||
|
"babel-preset-expo": "~8.0.0", |
||||
|
"eslint": "^6.8.0", |
||||
|
"eslint-config-airbnb": "^18.0.1", |
||||
|
"eslint-config-prettier": "^6.10.0", |
||||
|
"eslint-plugin-import": "^2.20.1", |
||||
|
"eslint-plugin-jsx-a11y": "^6.2.3", |
||||
|
"eslint-plugin-react": "^7.18.3", |
||||
|
"prettier": "^1.19.1" |
||||
|
}, |
||||
|
"private": true |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
import axios from 'axios'; |
||||
|
import { getEnvVars } from '../../Environment'; |
||||
|
|
||||
|
const { apiUrl } = getEnvVars(); |
||||
|
|
||||
|
const axiosInstance = axios.create({ |
||||
|
baseURL: apiUrl, |
||||
|
}); |
||||
|
|
||||
|
export default axiosInstance; |
||||
@ -0,0 +1,41 @@ |
|||||
|
import api from './API'; |
||||
|
import { getEnvVars } from '../../Environment'; |
||||
|
|
||||
|
const { oAuthConfig } = getEnvVars(); |
||||
|
|
||||
|
export const login = ({ username, password }) => { |
||||
|
// eslint-disable-next-line no-undef
|
||||
|
const formData = new FormData(); |
||||
|
formData.append('username', username); |
||||
|
formData.append('password', password); |
||||
|
formData.append('grant_type', 'password'); |
||||
|
formData.append('scope', `${oAuthConfig.scope} offline_access`); |
||||
|
formData.append('client_id', oAuthConfig.clientId); |
||||
|
formData.append('client_secret', oAuthConfig.clientSecret); |
||||
|
|
||||
|
return api({ |
||||
|
method: 'POST', |
||||
|
url: '/connect/token', |
||||
|
headers: { 'Content-Type': 'multipart/form-data' }, |
||||
|
data: formData, |
||||
|
baseURL: oAuthConfig.issuer, |
||||
|
}).then(({ data }) => data); |
||||
|
}; |
||||
|
|
||||
|
export const logout = () => |
||||
|
api({ |
||||
|
method: 'GET', |
||||
|
url: '/api/account/logout', |
||||
|
}).then(({ data }) => data); |
||||
|
|
||||
|
export const getTenant = tenantName => |
||||
|
api({ |
||||
|
method: 'GET', |
||||
|
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`, |
||||
|
}).then(({ data }) => data); |
||||
|
|
||||
|
export const getTenantById = tenantId => |
||||
|
api({ |
||||
|
method: 'GET', |
||||
|
url: `/api/abp/multi-tenancy/tenants/by-id/${tenantId}`, |
||||
|
}).then(({ data }) => data); |
||||
@ -0,0 +1,30 @@ |
|||||
|
import i18n from 'i18n-js'; |
||||
|
import api from './API'; |
||||
|
|
||||
|
export const getApplicationConfiguration = () => |
||||
|
api |
||||
|
.get('/api/abp/application-configuration') |
||||
|
.then(({ data }) => data) |
||||
|
.then(async config => { |
||||
|
const { cultureName } = config.localization.currentCulture; |
||||
|
i18n.locale = cultureName; |
||||
|
|
||||
|
Object.keys(config.localization.values).forEach(key => { |
||||
|
const resource = config.localization.values[key]; |
||||
|
|
||||
|
if (typeof resource !== 'object') return; |
||||
|
|
||||
|
Object.keys(resource).forEach(key2 => { |
||||
|
if (/'{|{/g.test(resource[key2])) { |
||||
|
resource[key2] = resource[key2].replace(/'{|{/g, '{{').replace(/}'|}/g, '}}'); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
i18n.translations[cultureName] = { |
||||
|
...config.localization.values, |
||||
|
...(i18n.translations[cultureName] || {}), |
||||
|
}; |
||||
|
|
||||
|
return config; |
||||
|
}); |
||||
@ -0,0 +1,26 @@ |
|||||
|
import api from './API'; |
||||
|
|
||||
|
export const getProfileDetail = () => api.get('/api/identity/my-profile').then(({ data }) => data); |
||||
|
|
||||
|
export const getAllRoles = () => api.get('/api/identity/roles/all').then(({ data }) => data.items); |
||||
|
|
||||
|
export const getUserRoles = id => |
||||
|
api.get(`/api/identity/users/${id}/roles`).then(({ data }) => data.items); |
||||
|
|
||||
|
export const getUsers = (params = { maxResultCount: 10, skipCount: 0 }) => |
||||
|
api.get('/api/identity/users', { params }).then(({ data }) => data); |
||||
|
|
||||
|
export const getUserById = id => api.get(`/api/identity/users/${id}`).then(({ data }) => data); |
||||
|
|
||||
|
export const createUser = body => api.post('/api/identity/users', body).then(({ data }) => data); |
||||
|
|
||||
|
export const updateUser = (body, id) => |
||||
|
api.put(`/api/identity/users/${id}`, body).then(({ data }) => data); |
||||
|
|
||||
|
export const removeUser = id => api.delete(`/api/identity/users/${id}`); |
||||
|
|
||||
|
export const updateProfileDetail = body => |
||||
|
api.put('/api/identity/my-profile', body).then(({ data }) => data); |
||||
|
|
||||
|
export const changePassword = body => |
||||
|
api.post('/api/identity/my-profile/change-password', body).then(({ data }) => data); |
||||
@ -0,0 +1,21 @@ |
|||||
|
import api from './API'; |
||||
|
|
||||
|
export function getTenants(params = {}) { |
||||
|
return api.get('/api/multi-tenancy/tenants', { params }).then(({ data }) => data); |
||||
|
} |
||||
|
|
||||
|
export function createTenant(body) { |
||||
|
return api.post('/api/multi-tenancy/tenants', body).then(({ data }) => data); |
||||
|
} |
||||
|
|
||||
|
export function getTenantById(id) { |
||||
|
return api.get(`/api/multi-tenancy/tenants/${id}`).then(({ data }) => data); |
||||
|
} |
||||
|
|
||||
|
export function updateTenant(body, id) { |
||||
|
return api.put(`/api/multi-tenancy/tenants/${id}`, body).then(({ data }) => data); |
||||
|
} |
||||
|
|
||||
|
export function removeTenant(id) { |
||||
|
return api.delete(`/api/multi-tenancy/tenants/${id}`).then(({ data }) => data); |
||||
|
} |
||||
@ -0,0 +1,97 @@ |
|||||
|
import { Ionicons } from '@expo/vector-icons'; |
||||
|
import * as Font from 'expo-font'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useEffect, useState, useMemo } from 'react'; |
||||
|
import { Platform, StatusBar } from 'react-native'; |
||||
|
import { NavigationContainer } from '@react-navigation/native'; |
||||
|
import { Root } from 'native-base'; |
||||
|
import Loading from '../Loading/Loading'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import { createLanguageSelector } from '../../store/selectors/AppSelectors'; |
||||
|
import { createTokenSelector } from '../../store/selectors/PersistentStorageSelectors'; |
||||
|
import AppActions from '../../store/actions/AppActions'; |
||||
|
import PersistentStorageActions from '../../store/actions/PersistentStorageActions'; |
||||
|
import { LocalizationContext } from '../../contexts/LocalizationContext'; |
||||
|
import { isTokenValid } from '../../utils/TokenUtils'; |
||||
|
import DrawerNavigator from '../../navigators/DrawerNavigator'; |
||||
|
import AuthNavigator from '../../navigators/AuthNavigator'; |
||||
|
import { getEnvVars } from '../../../Environment'; |
||||
|
|
||||
|
const { localization } = getEnvVars(); |
||||
|
|
||||
|
i18n.defaultSeparator = '::'; |
||||
|
|
||||
|
const cloneT = i18n.t; |
||||
|
i18n.t = (key, ...args) => { |
||||
|
if (key.slice(0, 2) === '::') { |
||||
|
key = localization.defaultResourceName + key; |
||||
|
} |
||||
|
return cloneT(key, ...args); |
||||
|
}; |
||||
|
|
||||
|
function AppContainer({ language, fetchAppConfig, token, setToken }) { |
||||
|
const platform = Platform.OS; |
||||
|
const [isReady, setIsReady] = useState(false); |
||||
|
|
||||
|
const localizationContext = useMemo( |
||||
|
() => ({ |
||||
|
t: i18n.t, |
||||
|
locale: (language || {}).cultureName, |
||||
|
}), |
||||
|
[language], |
||||
|
); |
||||
|
|
||||
|
const isValid = useMemo(() => isTokenValid(token), [token]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (!isValid && token && token.access_token) { |
||||
|
setToken({}); |
||||
|
} |
||||
|
}, [isValid]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
fetchAppConfig(); |
||||
|
|
||||
|
Font.loadAsync({ |
||||
|
Roboto: require('native-base/Fonts/Roboto.ttf'), |
||||
|
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'), |
||||
|
...Ionicons.font, |
||||
|
}).then(() => setIsReady(true)); |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<StatusBar barStyle={platform === 'ios' ? 'dark-content' : 'light-content'} /> |
||||
|
<Root> |
||||
|
{isReady && language ? ( |
||||
|
<LocalizationContext.Provider value={localizationContext}> |
||||
|
<NavigationContainer> |
||||
|
{isValid ? <DrawerNavigator /> : <AuthNavigator />} |
||||
|
</NavigationContainer> |
||||
|
</LocalizationContext.Provider> |
||||
|
) : null} |
||||
|
</Root> |
||||
|
<Loading /> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
AppContainer.propTypes = { |
||||
|
language: PropTypes.object, |
||||
|
token: PropTypes.object.isRequired, |
||||
|
fetchAppConfig: PropTypes.func.isRequired, |
||||
|
setToken: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: AppContainer, |
||||
|
stateProps: state => ({ |
||||
|
language: createLanguageSelector()(state), |
||||
|
token: createTokenSelector()(state), |
||||
|
}), |
||||
|
dispatchProps: { |
||||
|
fetchAppConfig: AppActions.fetchAppConfigAsync, |
||||
|
setToken: PersistentStorageActions.setToken, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,139 @@ |
|||||
|
import { useFocusEffect } from '@react-navigation/native'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { connectStyle, Icon, Input, InputGroup, Item, List, Spinner, Text } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { forwardRef, useCallback, useEffect, useState } from 'react'; |
||||
|
import { RefreshControl, StyleSheet, View } from 'react-native'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import { activeTheme } from '../../theme/variables'; |
||||
|
import { debounce } from '../../utils/Debounce'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import LoadingButton from '../LoadingButton/LoadingButton'; |
||||
|
|
||||
|
function DataList({ |
||||
|
style, |
||||
|
navigation, |
||||
|
fetchFn, |
||||
|
render, |
||||
|
maxResultCount = 15, |
||||
|
debounceTime = 350, |
||||
|
...props |
||||
|
}) { |
||||
|
const [records, setRecords] = useState([]); |
||||
|
const [totalCount, setTotalCount] = useState(0); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
const [searchLoading, setSearchLoading] = useState(false); |
||||
|
const [buttonLoading, setButtonLoading] = useState(false); |
||||
|
const [skipCount, setSkipCount] = useState(0); |
||||
|
const [filter, setFilter] = useState(''); |
||||
|
|
||||
|
const fetch = (skip = 0, isRefreshingActive = true) => { |
||||
|
if (isRefreshingActive) setLoading(true); |
||||
|
return fetchFn({ filter, maxResultCount, skipCount: skip }) |
||||
|
.then(({ items, totalCount: total }) => { |
||||
|
setTotalCount(total); |
||||
|
setRecords(skip ? [...records, ...items] : items); |
||||
|
setSkipCount(skip); |
||||
|
}) |
||||
|
.finally(() => { |
||||
|
if (isRefreshingActive) setLoading(false); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const fetchPartial = () => { |
||||
|
if (loading || records.length === totalCount) return; |
||||
|
|
||||
|
setButtonLoading(true); |
||||
|
fetch(skipCount + maxResultCount, false).finally(() => setButtonLoading(false)); |
||||
|
}; |
||||
|
|
||||
|
useFocusEffect( |
||||
|
useCallback(() => { |
||||
|
setSkipCount(0); |
||||
|
fetch(0, false); |
||||
|
}, []), |
||||
|
); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
function searchFetch() { |
||||
|
setSearchLoading(true); |
||||
|
return fetch(0, false).finally(() => setTimeout(() => setSearchLoading(false), 150)); |
||||
|
} |
||||
|
debounce(searchFetch, debounceTime)(); |
||||
|
}, [filter]); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Item placeholderLabel style={{ backgroundColor: '#fff' }}> |
||||
|
<InputGroup style={{ marginLeft: 10 }}> |
||||
|
<Input |
||||
|
placeholder={i18n.t('AbpUi::PagerSearch')} |
||||
|
style={{ padding: 0, margin: 0 }} |
||||
|
returnKeyType="done" |
||||
|
value={filter} |
||||
|
onChangeText={setFilter} |
||||
|
/> |
||||
|
{searchLoading ? ( |
||||
|
<View> |
||||
|
<Spinner style={style.spinner} color={style.spinner.color} /> |
||||
|
</View> |
||||
|
) : ( |
||||
|
<Icon style={{ fontSize: 20, marginRight: 15 }} name="ios-search" /> |
||||
|
)} |
||||
|
</InputGroup> |
||||
|
</Item> |
||||
|
<View style={style.container}> |
||||
|
<List |
||||
|
showsVerticalScrollIndicator |
||||
|
scrollEnabled |
||||
|
refreshControl={<RefreshControl refreshing={loading} onRefresh={fetch} />} |
||||
|
dataArray={records} |
||||
|
renderRow={(data, sectionID, rowId, ...args) => ( |
||||
|
<> |
||||
|
{render(data, sectionID, rowId, ...args)} |
||||
|
{rowId + 1 === skipCount + maxResultCount && totalCount > records.length ? ( |
||||
|
<View style={{ justifyContent: 'center', alignItems: 'center' }}> |
||||
|
<LoadingButton loading={buttonLoading} onPress={() => fetchPartial()}> |
||||
|
<Text>{i18n.t('AbpUi::LoadMore')}</Text> |
||||
|
</LoadingButton> |
||||
|
</View> |
||||
|
) : null} |
||||
|
</> |
||||
|
)} |
||||
|
{...props} |
||||
|
/> |
||||
|
</View> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
DataList.propTypes = { |
||||
|
...List.propTypes, |
||||
|
style: PropTypes.any.isRequired, |
||||
|
fetchFn: PropTypes.func.isRequired, |
||||
|
render: PropTypes.func.isRequired, |
||||
|
maxResultCount: PropTypes.number, |
||||
|
debounceTime: PropTypes.number, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { flex: 1 }, |
||||
|
list: {}, |
||||
|
spinner: { |
||||
|
transform: [{ scale: 0.5 }], |
||||
|
position: 'absolute', |
||||
|
right: 8, |
||||
|
top: -40, |
||||
|
color: activeTheme.brandInfo, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const Forwarded = forwardRef((props, ref) => <DataList {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: connectStyle('ABP.DataList', styles)(Forwarded), |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,121 @@ |
|||||
|
import { Text, View, List, ListItem, Left, Icon, Body } from 'native-base'; |
||||
|
import React from 'react'; |
||||
|
import { Image, StyleSheet } from 'react-native'; |
||||
|
import SafeAreaView from 'react-native-safe-area-view'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import Constants from 'expo-constants'; |
||||
|
import { withPermission } from '../../hocs/PermissionHOC'; |
||||
|
|
||||
|
const screens = { |
||||
|
Home: { label: '::Menu:Home', iconName: 'home' }, |
||||
|
Users: { |
||||
|
label: 'AbpIdentity::Users', |
||||
|
iconName: 'contacts', |
||||
|
requiredPolicy: 'AbpIdentity.Users', |
||||
|
}, |
||||
|
Tenants: { |
||||
|
label: 'AbpTenantManagement::Tenants', |
||||
|
iconName: 'people', |
||||
|
requiredPolicy: 'AbpTenantManagement.Tenants', |
||||
|
}, |
||||
|
Settings: { label: 'AbpSettingManagement::Settings', iconName: 'cog' }, |
||||
|
}; |
||||
|
|
||||
|
const ListItemWithPermission = withPermission(ListItem); |
||||
|
|
||||
|
function DrawerContent({ navigation, state: { routeNames, index: currentScreenIndex } }) { |
||||
|
const navigate = screen => { |
||||
|
navigation.navigate(screen); |
||||
|
navigation.closeDrawer(); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<View style={styles.container}> |
||||
|
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}> |
||||
|
<View style={styles.headerView}> |
||||
|
<Image style={styles.logo} source={require('../../../assets/logo.png')} /> |
||||
|
</View> |
||||
|
<List |
||||
|
dataArray={routeNames} |
||||
|
keyExtractor={item => item} |
||||
|
renderRow={name => ( |
||||
|
<ListItemWithPermission |
||||
|
icon |
||||
|
key={name} |
||||
|
policyKey={screens[name].requiredPolicy} |
||||
|
selected={name === routeNames[currentScreenIndex]} |
||||
|
onPress={() => navigate(name)} |
||||
|
style={{ |
||||
|
...styles.navItem, |
||||
|
backgroundColor: name === routeNames[currentScreenIndex] ? '#38003c' : '#f2f2f2', |
||||
|
}}> |
||||
|
<Left> |
||||
|
<Icon |
||||
|
dark={name !== routeNames[currentScreenIndex]} |
||||
|
light={name === routeNames[currentScreenIndex]} |
||||
|
name={screens[name].iconName} |
||||
|
/> |
||||
|
</Left> |
||||
|
<Body style={{ borderBottomWidth: 0 }}> |
||||
|
<Text |
||||
|
style={{ |
||||
|
color: name === routeNames[currentScreenIndex] ? '#fff' : '#000', |
||||
|
}}> |
||||
|
{i18n.t(screens[name].label)} |
||||
|
</Text> |
||||
|
</Body> |
||||
|
</ListItemWithPermission> |
||||
|
)} |
||||
|
/> |
||||
|
</SafeAreaView> |
||||
|
<View style={styles.footer}> |
||||
|
<Text note style={styles.copyRight}> |
||||
|
© MyProjectName |
||||
|
</Text> |
||||
|
<Text note style={styles.version}> |
||||
|
v{Constants.manifest.version} |
||||
|
</Text> |
||||
|
</View> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
flexGrow: 1, |
||||
|
}, |
||||
|
logo: { |
||||
|
marginTop: 20, |
||||
|
marginBottom: 15, |
||||
|
}, |
||||
|
headerView: { |
||||
|
borderBottomWidth: 1, |
||||
|
borderColor: '#eee', |
||||
|
alignItems: 'center', |
||||
|
}, |
||||
|
navItem: { |
||||
|
marginLeft: 0, |
||||
|
marginBottom: 3, |
||||
|
paddingLeft: 10, |
||||
|
width: '100%', |
||||
|
backgroundColor: '#f2f2f2', |
||||
|
}, |
||||
|
footer: { |
||||
|
backgroundColor: '#eee', |
||||
|
flexDirection: 'row', |
||||
|
justifyContent: 'space-between', |
||||
|
}, |
||||
|
copyRight: { |
||||
|
margin: 15, |
||||
|
}, |
||||
|
version: { |
||||
|
margin: 15, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
DrawerContent.propTypes = { |
||||
|
state: PropTypes.object.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default DrawerContent; |
||||
@ -0,0 +1,82 @@ |
|||||
|
import React, { forwardRef } from 'react'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { Button, Text, connectStyle } from 'native-base'; |
||||
|
import { View, StyleSheet, Alert } from 'react-native'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
|
||||
|
function FormButtons({ |
||||
|
style, |
||||
|
submit, |
||||
|
remove, |
||||
|
removeMessage, |
||||
|
isRemoveDisabled, |
||||
|
isSubmitDisabled, |
||||
|
isShowRemove = false, |
||||
|
isShowSubmit = true, |
||||
|
}) { |
||||
|
const confirmation = () => { |
||||
|
Alert.alert( |
||||
|
i18n.t('AbpUi::AreYouSure'), |
||||
|
removeMessage, |
||||
|
[ |
||||
|
{ |
||||
|
text: i18n.t('AbpUi::Cancel'), |
||||
|
style: 'cancel', |
||||
|
}, |
||||
|
{ text: i18n.t('AbpUi::Yes'), onPress: () => remove() }, |
||||
|
], |
||||
|
{ cancelable: true }, |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<View style={style.container}> |
||||
|
{isShowRemove ? ( |
||||
|
<Button |
||||
|
abpButton |
||||
|
danger |
||||
|
style={{ flex: 1, borderRadius: 0 }} |
||||
|
onPress={() => confirmation()} |
||||
|
disabled={isRemoveDisabled}> |
||||
|
<Text>{i18n.t('AbpIdentity::Delete')}</Text> |
||||
|
</Button> |
||||
|
) : null} |
||||
|
{isShowSubmit ? ( |
||||
|
<Button |
||||
|
abpButton |
||||
|
primary |
||||
|
style={{ flex: 1, borderRadius: 0 }} |
||||
|
onPress={submit} |
||||
|
disabled={isSubmitDisabled}> |
||||
|
<Text>{i18n.t('AbpIdentity::Save')}</Text> |
||||
|
</Button> |
||||
|
) : null} |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
FormButtons.propTypes = { |
||||
|
submit: PropTypes.func.isRequired, |
||||
|
remove: PropTypes.func, |
||||
|
removeMessage: PropTypes.string, |
||||
|
style: PropTypes.any, |
||||
|
isRemoveDisabled: PropTypes.bool, |
||||
|
isSubmitDisabled: PropTypes.bool, |
||||
|
isShowRemove: PropTypes.bool, |
||||
|
isShowSubmit: PropTypes.bool, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
width: '100%', |
||||
|
justifyContent: 'center', |
||||
|
alignItems: 'center', |
||||
|
position: 'absolute', |
||||
|
bottom: 0, |
||||
|
flexDirection: 'row', |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const Forwarded = forwardRef((props, ref) => <FormButtons {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
export default connectStyle('ABP.FormButtons', styles)(Forwarded); |
||||
@ -0,0 +1,63 @@ |
|||||
|
import React, { forwardRef } from 'react'; |
||||
|
import { Spinner, View, connectStyle } from 'native-base'; |
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { activeTheme } from '../../theme/variables'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import { |
||||
|
createLoadingSelector, |
||||
|
createOpacitySelector, |
||||
|
} from '../../store/selectors/LoadingSelectors'; |
||||
|
|
||||
|
function Loading({ style, loading, opacity }) { |
||||
|
return loading ? ( |
||||
|
<View style={style.container}> |
||||
|
<View |
||||
|
style={{ |
||||
|
...style.backdrop, |
||||
|
opacity: opacity || 0.6, |
||||
|
}} |
||||
|
/> |
||||
|
<Spinner style={style.spinner} color={style.spinner.color} /> |
||||
|
</View> |
||||
|
) : null; |
||||
|
} |
||||
|
const Forwarded = forwardRef((props, ref) => <Loading {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
const backdropStyle = { |
||||
|
position: 'absolute', |
||||
|
top: 0, |
||||
|
left: 0, |
||||
|
width: '100%', |
||||
|
height: '100%', |
||||
|
backgroundColor: '#fff', |
||||
|
}; |
||||
|
|
||||
|
export const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
...backdropStyle, |
||||
|
backgroundColor: 'transparent', |
||||
|
zIndex: activeTheme.zIndex.indicator, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
}, |
||||
|
backdrop: backdropStyle, |
||||
|
spinner: { |
||||
|
color: activeTheme.brandPrimary, |
||||
|
fontSize: 100, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
Loading.propTypes = { |
||||
|
style: PropTypes.objectOf(PropTypes.any), |
||||
|
loading: PropTypes.bool, |
||||
|
opacity: PropTypes.number, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: connectStyle('ABP.Loading', styles)(Forwarded), |
||||
|
stateProps: state => ({ |
||||
|
loading: createLoadingSelector()(state), |
||||
|
opacity: createOpacitySelector()(state), |
||||
|
}), |
||||
|
}); |
||||
@ -0,0 +1,31 @@ |
|||||
|
import { Button, connectStyle, Spinner } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { forwardRef } from 'react'; |
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
|
||||
|
function LoadingButton({ loading = false, style, children, ...props }) { |
||||
|
return ( |
||||
|
<Button style={style.button} {...props}> |
||||
|
{children} |
||||
|
{loading ? <Spinner style={style.spinner} color={style.spinner.color || 'white'} /> : null} |
||||
|
</Button> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
LoadingButton.propTypes = { |
||||
|
...Button.propTypes, |
||||
|
loading: PropTypes.bool.isRequired, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
button: { marginTop: 20, marginBottom: 30, height: 30 }, |
||||
|
spinner: { |
||||
|
transform: [{ scale: 0.5 }], |
||||
|
color: 'white', |
||||
|
marginRight: 5, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const Forwarded = forwardRef((props, ref) => <LoadingButton {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
export default connectStyle('ABP.LoadingButton', styles)(Forwarded); |
||||
@ -0,0 +1,19 @@ |
|||||
|
import React from 'react'; |
||||
|
import { TouchableOpacity } from 'react-native'; |
||||
|
import { Icon } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
|
||||
|
function MenuIcon({ onPress, iconName = 'menu' }) { |
||||
|
return ( |
||||
|
<TouchableOpacity onPress={onPress}> |
||||
|
<Icon navElement name={iconName} /> |
||||
|
</TouchableOpacity> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
MenuIcon.propTypes = { |
||||
|
onPress: PropTypes.func.isRequired, |
||||
|
iconName: PropTypes.string, |
||||
|
}; |
||||
|
|
||||
|
export default MenuIcon; |
||||
@ -0,0 +1,128 @@ |
|||||
|
import i18n from 'i18n-js'; |
||||
|
import { |
||||
|
Button, |
||||
|
connectStyle, |
||||
|
Content, |
||||
|
Input, |
||||
|
InputGroup, |
||||
|
Label, |
||||
|
Segment, |
||||
|
Text, |
||||
|
} from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { forwardRef, useState } from 'react'; |
||||
|
import { StyleSheet, View, Alert } from 'react-native'; |
||||
|
import { getTenant } from '../../api/AccountAPI'; |
||||
|
import PersistentStorageActions from '../../store/actions/PersistentStorageActions'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import { createTenantSelector } from '../../store/selectors/PersistentStorageSelectors'; |
||||
|
|
||||
|
function TenantBox({ style, tenant = {}, setTenant, showTenantSelection, toggleTenantSelection }) { |
||||
|
const [tenantName, setTenantName] = useState(tenant.name); |
||||
|
|
||||
|
const findTenant = () => { |
||||
|
if (!tenantName) { |
||||
|
setTenant({}); |
||||
|
toggleTenantSelection(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
getTenant(tenantName).then(({ success, ...data }) => { |
||||
|
if (!success) { |
||||
|
Alert.alert( |
||||
|
i18n.t('AbpUi::Error'), |
||||
|
i18n.t('AbpUiMultiTenancy::GivenTenantIsNotAvailable', { |
||||
|
0: tenantName, |
||||
|
}), |
||||
|
[{ text: i18n.t('AbpUi::Ok') }], |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
setTenant(data); |
||||
|
toggleTenantSelection(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Segment style={style.container}> |
||||
|
<View> |
||||
|
<Text style={style.title}>{i18n.t('AbpUiMultiTenancy::Tenant')}</Text> |
||||
|
<Text style={style.tenant}> |
||||
|
{tenant.name ? tenant.name : i18n.t('AbpUiMultiTenancy::NotSelected')} |
||||
|
</Text> |
||||
|
</View> |
||||
|
<Button |
||||
|
style={{ ...style.switchButton, display: !showTenantSelection ? 'flex' : 'none' }} |
||||
|
onPress={() => toggleTenantSelection()}> |
||||
|
<Text style={{ color: '#fff' }}>{i18n.t('AbpUiMultiTenancy::Switch')}</Text> |
||||
|
</Button> |
||||
|
</Segment> |
||||
|
{showTenantSelection ? ( |
||||
|
<Content px20 style={{ flex: 1 }}> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpUiMultiTenancy::Name')}</Label> |
||||
|
<Input abpInput value={tenantName} onChangeText={setTenantName} /> |
||||
|
</InputGroup> |
||||
|
<Text style={style.hint}>{i18n.t('AbpUiMultiTenancy::SwitchTenantHint')}</Text> |
||||
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> |
||||
|
<Button abpButton light style={style.button} onPress={() => toggleTenantSelection()}> |
||||
|
<Text>{i18n.t('AbpAccount::Cancel')}</Text> |
||||
|
</Button> |
||||
|
<Button abpButton style={style.button} onPress={() => findTenant()}> |
||||
|
<Text>{i18n.t('AbpAccount::Save')}</Text> |
||||
|
</Button> |
||||
|
</View> |
||||
|
</Content> |
||||
|
) : null} |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
TenantBox.propTypes = { |
||||
|
style: PropTypes.any.isRequired, |
||||
|
setTenant: PropTypes.func.isRequired, |
||||
|
showTenantSelection: PropTypes.bool.isRequired, |
||||
|
toggleTenantSelection: PropTypes.func.isRequired, |
||||
|
tenant: PropTypes.object.isRequired, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
paddingHorizontal: 20, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'space-between', |
||||
|
height: 70, |
||||
|
}, |
||||
|
button: { marginTop: 20, width: '49%' }, |
||||
|
switchButton: { |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderBottomWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderRadius: 10, |
||||
|
backgroundColor: '#38003c', |
||||
|
height: 35, |
||||
|
}, |
||||
|
tenant: { color: '#777' }, |
||||
|
title: { |
||||
|
marginRight: 10, |
||||
|
color: '#777', |
||||
|
fontSize: 13, |
||||
|
fontWeight: '600', |
||||
|
textTransform: 'uppercase', |
||||
|
}, |
||||
|
hint: { color: '#bbb', textAlign: 'left' }, |
||||
|
}); |
||||
|
|
||||
|
const Forwarded = forwardRef((props, ref) => <TenantBox {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: connectStyle('ABP.TenantBox', styles)(Forwarded), |
||||
|
dispatchProps: { |
||||
|
setTenant: PersistentStorageActions.setTenant, |
||||
|
}, |
||||
|
stateProps: state => ({ |
||||
|
tenant: createTenantSelector()(state), |
||||
|
}), |
||||
|
}); |
||||
@ -0,0 +1,18 @@ |
|||||
|
import i18n from 'i18n-js'; |
||||
|
import { connectStyle } from 'native-base'; |
||||
|
import React, { forwardRef } from 'react'; |
||||
|
import { Text } from 'react-native'; |
||||
|
|
||||
|
const ValidationMessage = ({ children, ...props }) => |
||||
|
children ? <Text {...props}>{i18n.t(children)}</Text> : null; |
||||
|
|
||||
|
const styles = { |
||||
|
fontSize: 12, |
||||
|
marginHorizontal: 10, |
||||
|
marginTop: -5, |
||||
|
color: '#ed2f2f', |
||||
|
}; |
||||
|
|
||||
|
const Forwarded = forwardRef((props, ref) => <ValidationMessage {...props} forwardedRef={ref} />); |
||||
|
|
||||
|
export default connectStyle('ABP.ValidationMessage', styles)(Forwarded); |
||||
@ -0,0 +1,3 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
export const LocalizationContext = React.createContext(); |
||||
@ -0,0 +1,17 @@ |
|||||
|
import React, { forwardRef } from 'react'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { usePermission } from '../hooks/UsePermission'; |
||||
|
|
||||
|
export function withPermission(Component, policyKey) { |
||||
|
const Forwarded = forwardRef((props, ref) => { |
||||
|
const isGranted = |
||||
|
policyKey || props.policyKey ? usePermission(policyKey || props.policyKey) : true; |
||||
|
return isGranted ? <Component ref={ref} {...props} /> : null; |
||||
|
}); |
||||
|
|
||||
|
Forwarded.propTypes = { |
||||
|
policyKey: PropTypes.string, |
||||
|
}; |
||||
|
|
||||
|
return Forwarded; |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import { useEffect, useState } from 'react'; |
||||
|
import { store } from '../store'; |
||||
|
import { createGrantedPolicySelector } from '../store/selectors/AppSelectors'; |
||||
|
|
||||
|
export function usePermission(key) { |
||||
|
const [permission, setPermission] = useState(false); |
||||
|
|
||||
|
const state = store.getState(); |
||||
|
const policy = createGrantedPolicySelector(key)(state); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
setPermission(policy); |
||||
|
}, [policy]); |
||||
|
|
||||
|
return permission; |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
import { Toast } from 'native-base'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import api from '../api/API'; |
||||
|
import PersistentStorageActions from '../store/actions/PersistentStorageActions'; |
||||
|
import LoadingActions from '../store/actions/LoadingActions'; |
||||
|
|
||||
|
export function initAPIInterceptor(store) { |
||||
|
api.interceptors.request.use( |
||||
|
async request => { |
||||
|
const { |
||||
|
persistentStorage: { token, language, tenant }, |
||||
|
} = store.getState(); |
||||
|
|
||||
|
if (!request.headers.Authorization && token && token.access_token) { |
||||
|
request.headers.Authorization = `${token.token_type} ${token.access_token}`; |
||||
|
} |
||||
|
|
||||
|
if (!request.headers['Content-Type']) { |
||||
|
request.headers['Content-Type'] = 'application/json'; |
||||
|
} |
||||
|
|
||||
|
if (!request.headers['Accept-Language'] && language) { |
||||
|
request.headers['Accept-Language'] = language; |
||||
|
} |
||||
|
|
||||
|
if (!request.headers.__tenant && tenant && tenant.tenantId) { |
||||
|
request.headers.__tenant = tenant.tenantId; |
||||
|
} |
||||
|
|
||||
|
return request; |
||||
|
}, |
||||
|
error => console.error(error), |
||||
|
); |
||||
|
|
||||
|
api.interceptors.response.use( |
||||
|
response => response, |
||||
|
error => { |
||||
|
store.dispatch(LoadingActions.clear()); |
||||
|
const errorRes = error.response; |
||||
|
if (errorRes) { |
||||
|
if (errorRes.headers._abperrorformat && errorRes.status === 401) { |
||||
|
store.dispatch(PersistentStorageActions.setToken({})); |
||||
|
} |
||||
|
|
||||
|
showError({ error: errorRes.data.error || {}, status: errorRes.status }); |
||||
|
} else { |
||||
|
Toast.show({ |
||||
|
text: 'An unexpected error has occurred', |
||||
|
buttonText: 'x', |
||||
|
duration: 10000, |
||||
|
type: 'danger', |
||||
|
textStyle: { textAlign: 'center' }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return Promise.reject(error); |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
function showError({ error = {}, status }) { |
||||
|
let message = ''; |
||||
|
let title = i18n.t('AbpAccount::DefaultErrorMessage'); |
||||
|
|
||||
|
if (typeof error === 'string') { |
||||
|
message = error; |
||||
|
} else if (error.details) { |
||||
|
message = error.details; |
||||
|
title = error.message; |
||||
|
} else if (error.message) { |
||||
|
message = error.message; |
||||
|
} else { |
||||
|
switch (status) { |
||||
|
case 401: |
||||
|
title = i18n.t('AbpAccount::DefaultErrorMessage401'); |
||||
|
message = i18n.t('AbpAccount::DefaultErrorMessage401Detail'); |
||||
|
break; |
||||
|
case 403: |
||||
|
title = i18n.t('AbpAccount::DefaultErrorMessage403'); |
||||
|
message = i18n.t('AbpAccount::DefaultErrorMessage403Detail'); |
||||
|
break; |
||||
|
case 404: |
||||
|
title = i18n.t('AbpAccount::DefaultErrorMessage404'); |
||||
|
message = i18n.t('AbpAccount::DefaultErrorMessage404Detail'); |
||||
|
break; |
||||
|
case 500: |
||||
|
title = i18n.t('AbpAccount::500Message'); |
||||
|
message = i18n.t('AbpAccount::InternalServerErrorMessage'); |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Toast.show({ |
||||
|
text: `${title}\n${message}`, |
||||
|
buttonText: 'x', |
||||
|
duration: 10000, |
||||
|
type: 'danger', |
||||
|
textStyle: { textAlign: 'center' }, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
import { createStackNavigator } from '@react-navigation/stack'; |
||||
|
import React from 'react'; |
||||
|
import { LocalizationContext } from '../contexts/LocalizationContext'; |
||||
|
import LoginScreen from '../screens/Login/LoginScreen'; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
export default function AuthStackNavigator() { |
||||
|
const { t } = React.useContext(LocalizationContext); |
||||
|
|
||||
|
return ( |
||||
|
<Stack.Navigator initialRouteName="Login"> |
||||
|
<Stack.Screen |
||||
|
name="Login" |
||||
|
component={LoginScreen} |
||||
|
options={() => ({ |
||||
|
title: t('AbpAccount::Login'), |
||||
|
})} |
||||
|
/> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
import React from 'react'; |
||||
|
import { createDrawerNavigator } from '@react-navigation/drawer'; |
||||
|
import HomeStackNavigator from './HomeNavigator'; |
||||
|
import SettingsStackNavigator from './SettingsNavigator'; |
||||
|
import UsersStackNavigator from './UsersNavigator'; |
||||
|
import TenantsStackNavigator from './TenantsNavigator'; |
||||
|
import DrawerContent from '../components/DrawerContent/DrawerContent'; |
||||
|
|
||||
|
const Drawer = createDrawerNavigator(); |
||||
|
|
||||
|
export default function DrawerNavigator() { |
||||
|
return ( |
||||
|
<Drawer.Navigator initialRouteName="Home" drawerContent={DrawerContent}> |
||||
|
<Drawer.Screen name="Home" component={HomeStackNavigator} /> |
||||
|
<Drawer.Screen name="Users" component={UsersStackNavigator} /> |
||||
|
<Drawer.Screen name="Tenants" component={TenantsStackNavigator} /> |
||||
|
<Drawer.Screen name="Settings" component={SettingsStackNavigator} /> |
||||
|
</Drawer.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
import React from 'react'; |
||||
|
import { createStackNavigator } from '@react-navigation/stack'; |
||||
|
import HomeScreen from '../screens/Home/HomeScreen'; |
||||
|
import MenuIcon from '../components/MenuIcon/MenuIcon'; |
||||
|
import { LocalizationContext } from '../contexts/LocalizationContext'; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
export default function HomeStackNavigator() { |
||||
|
const { t } = React.useContext(LocalizationContext); |
||||
|
|
||||
|
return ( |
||||
|
<Stack.Navigator initialRouteName="Home"> |
||||
|
<Stack.Screen |
||||
|
name="Home" |
||||
|
component={HomeScreen} |
||||
|
options={({ navigation }) => ({ |
||||
|
headerLeft: () => <MenuIcon onPress={() => navigation.openDrawer()} />, |
||||
|
title: t('::Menu:Home'), |
||||
|
})} |
||||
|
/> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
import React from 'react'; |
||||
|
import { createStackNavigator } from '@react-navigation/stack'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import SettingsScreen from '../screens/Settings/SettingsScreen'; |
||||
|
import ChangePasswordScreen from '../screens/ChangePassword/ChangePasswordScreen'; |
||||
|
import ManageProfileScreen from '../screens/ManageProfile/ManageProfileScreen'; |
||||
|
import MenuIcon from '../components/MenuIcon/MenuIcon'; |
||||
|
import { LocalizationContext } from '../contexts/LocalizationContext'; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
export default function SettingsStackNavigator() { |
||||
|
const { t } = React.useContext(LocalizationContext); |
||||
|
|
||||
|
return ( |
||||
|
<Stack.Navigator initialRouteName="Settings"> |
||||
|
<Stack.Screen |
||||
|
name="Settings" |
||||
|
component={SettingsScreen} |
||||
|
options={({ navigation }) => ({ |
||||
|
headerLeft: () => <MenuIcon onPress={() => navigation.openDrawer()} />, |
||||
|
title: t('AbpSettingManagement::Settings'), |
||||
|
})} |
||||
|
/> |
||||
|
<Stack.Screen |
||||
|
name="ChangePassword" |
||||
|
component={ChangePasswordScreen} |
||||
|
options={{ |
||||
|
title: i18n.t('AbpUi::ChangePassword'), |
||||
|
}} |
||||
|
/> |
||||
|
<Stack.Screen |
||||
|
name="ManageProfile" |
||||
|
component={ManageProfileScreen} |
||||
|
options={{ |
||||
|
title: i18n.t('AbpAccount::ManageYourProfile'), |
||||
|
}} |
||||
|
/> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
import React from 'react'; |
||||
|
import { createStackNavigator } from '@react-navigation/stack'; |
||||
|
import TenantsScreen from '../screens/Tenants/TenantsScreen'; |
||||
|
import CreateUpdateTenantScreen from '../screens/CreateUpdateTenant/CreateUpdateTenantScreen'; |
||||
|
import MenuIcon from '../components/MenuIcon/MenuIcon'; |
||||
|
import { LocalizationContext } from '../contexts/LocalizationContext'; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
export default function TenantsStackNavigator() { |
||||
|
const { t } = React.useContext(LocalizationContext); |
||||
|
|
||||
|
return ( |
||||
|
<Stack.Navigator initialRouteName="Tenants"> |
||||
|
<Stack.Screen |
||||
|
name="Tenants" |
||||
|
component={TenantsScreen} |
||||
|
options={({ navigation }) => ({ |
||||
|
headerLeft: () => <MenuIcon onPress={() => navigation.openDrawer()} />, |
||||
|
title: t('AbpTenantManagement::Tenants'), |
||||
|
})} |
||||
|
/> |
||||
|
<Stack.Screen |
||||
|
name="CreateUpdateTenant" |
||||
|
component={CreateUpdateTenantScreen} |
||||
|
options={({ route }) => ({ |
||||
|
title: t( |
||||
|
route.params?.tenantId ? 'AbpTenantManagement::Edit' : 'AbpTenantManagement::NewTenant', |
||||
|
), |
||||
|
})} |
||||
|
/> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
import React from 'react'; |
||||
|
import { createStackNavigator } from '@react-navigation/stack'; |
||||
|
import UsersScreen from '../screens/Users/UsersScreen'; |
||||
|
import CreateUpdateUserScreen from '../screens/CreateUpdateUser/CreateUpdateUserScreen'; |
||||
|
import MenuIcon from '../components/MenuIcon/MenuIcon'; |
||||
|
import { LocalizationContext } from '../contexts/LocalizationContext'; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
export default function UsersStackNavigator() { |
||||
|
const { t } = React.useContext(LocalizationContext); |
||||
|
|
||||
|
return ( |
||||
|
<Stack.Navigator initialRouteName="Users"> |
||||
|
<Stack.Screen |
||||
|
name="Users" |
||||
|
component={UsersScreen} |
||||
|
options={({ navigation }) => ({ |
||||
|
headerLeft: () => <MenuIcon onPress={() => navigation.openDrawer()} />, |
||||
|
title: t('AbpIdentity::Users'), |
||||
|
})} |
||||
|
/> |
||||
|
<Stack.Screen |
||||
|
name="CreateUpdateUser" |
||||
|
component={CreateUpdateUserScreen} |
||||
|
options={({ route }) => ({ |
||||
|
title: t(route.params?.userId ? 'AbpIdentity::Edit' : 'AbpIdentity::NewUser'), |
||||
|
})} |
||||
|
/> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
import { Formik } from 'formik'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { Container, Content, Form, Input, InputGroup, Item, Icon, Label } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useRef, useState } from 'react'; |
||||
|
import * as Yup from 'yup'; |
||||
|
import FormButtons from '../../components/FormButtons/FormButtons'; |
||||
|
import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; |
||||
|
|
||||
|
const ValidationSchema = Yup.object().shape({ |
||||
|
currentPassword: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
newPassword: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
}); |
||||
|
|
||||
|
function ChangePasswordForm({ submit, cancel }) { |
||||
|
const [showCurrentPassword, setShowCurrentPassword] = useState(false); |
||||
|
const [showNewPassword, setShowNewPassword] = useState(false); |
||||
|
|
||||
|
const currentPasswordRef = useRef(); |
||||
|
const newPasswordRef = useRef(); |
||||
|
|
||||
|
const onSubmit = values => { |
||||
|
submit({ |
||||
|
...values, |
||||
|
newPasswordConfirm: values.newPassword, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Formik |
||||
|
enableReinitialize |
||||
|
validationSchema={ValidationSchema} |
||||
|
initialValues={{ |
||||
|
currentPassword: '', |
||||
|
newPassword: '', |
||||
|
}} |
||||
|
onSubmit={values => onSubmit(values)}> |
||||
|
{({ handleChange, handleBlur, handleSubmit, values, errors, isValid }) => ( |
||||
|
<> |
||||
|
<Container> |
||||
|
<Content px20> |
||||
|
<Form> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:CurrentPassword')}</Label> |
||||
|
<Item abpInput> |
||||
|
<Input |
||||
|
ref={currentPasswordRef} |
||||
|
onSubmitEditing={() => newPasswordRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('currentPassword')} |
||||
|
onBlur={handleBlur('currentPassword')} |
||||
|
value={values.currentPassword} |
||||
|
textContentType="password" |
||||
|
secureTextEntry={!showCurrentPassword} |
||||
|
/> |
||||
|
<Icon |
||||
|
active |
||||
|
name={showCurrentPassword ? 'eye-off' : 'eye'} |
||||
|
onPress={() => setShowCurrentPassword(!showCurrentPassword)} |
||||
|
/> |
||||
|
</Item> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.currentPassword}</ValidationMessage> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:NewPassword')}</Label> |
||||
|
<Item abpInput> |
||||
|
<Input |
||||
|
ref={newPasswordRef} |
||||
|
returnKeyType="done" |
||||
|
onSubmitEditing={handleSubmit} |
||||
|
onChangeText={handleChange('newPassword')} |
||||
|
onBlur={handleBlur('newPassword')} |
||||
|
value={values.newPassword} |
||||
|
textContentType="newPassword" |
||||
|
secureTextEntry={!showNewPassword} |
||||
|
/> |
||||
|
<Icon |
||||
|
name={showNewPassword ? 'eye-off' : 'eye'} |
||||
|
onPress={() => setShowNewPassword(!showNewPassword)} |
||||
|
/> |
||||
|
</Item> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.newPassword}</ValidationMessage> |
||||
|
</Form> |
||||
|
</Content> |
||||
|
</Container> |
||||
|
<FormButtons submit={handleSubmit} cancel={cancel} isSubmitDisabled={!isValid} /> |
||||
|
</> |
||||
|
)} |
||||
|
</Formik> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
ChangePasswordForm.propTypes = { |
||||
|
submit: PropTypes.func.isRequired, |
||||
|
cancel: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default ChangePasswordForm; |
||||
@ -0,0 +1,33 @@ |
|||||
|
import React from 'react'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { changePassword } from '../../api/IdentityAPI'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import ChangePasswordForm from './ChangePasswordForm'; |
||||
|
|
||||
|
function ChangePasswordScreen({ navigation, startLoading, stopLoading }) { |
||||
|
const submit = data => { |
||||
|
startLoading({ key: 'changePassword' }); |
||||
|
|
||||
|
changePassword(data) |
||||
|
.then(() => { |
||||
|
navigation.goBack(); |
||||
|
}) |
||||
|
.finally(() => stopLoading({ key: 'changePassword' })); |
||||
|
}; |
||||
|
|
||||
|
return <ChangePasswordForm submit={submit} cancel={() => navigation.goBack()} />; |
||||
|
} |
||||
|
|
||||
|
ChangePasswordScreen.propTypes = { |
||||
|
startLoading: PropTypes.func.isRequired, |
||||
|
stopLoading: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: ChangePasswordScreen, |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,84 @@ |
|||||
|
import { Formik } from 'formik'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { Container, Content, Input, InputGroup, Label } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useRef } from 'react'; |
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
import * as Yup from 'yup'; |
||||
|
import FormButtons from '../../components/FormButtons/FormButtons'; |
||||
|
import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; |
||||
|
import { usePermission } from '../../hooks/UsePermission'; |
||||
|
|
||||
|
const validations = { |
||||
|
name: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
}; |
||||
|
|
||||
|
function CreateUpdateTenantForm({ editingTenant = {}, submit, remove }) { |
||||
|
const tenantNameRef = useRef(); |
||||
|
|
||||
|
const hasRemovePermission = usePermission('AbpTenantManagement.Tenants.Delete'); |
||||
|
|
||||
|
const onSubmit = values => { |
||||
|
submit({ |
||||
|
...editingTenant, |
||||
|
...values, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Formik |
||||
|
enableReinitialize |
||||
|
validationSchema={Yup.object().shape({ |
||||
|
...validations, |
||||
|
})} |
||||
|
initialValues={{ |
||||
|
lockoutEnabled: false, |
||||
|
twoFactorEnabled: false, |
||||
|
...editingTenant, |
||||
|
}} |
||||
|
onSubmit={values => onSubmit(values)}> |
||||
|
{({ handleChange, handleBlur, handleSubmit, values, errors, isValid }) => ( |
||||
|
<> |
||||
|
<Container style={styles.container}> |
||||
|
<Content px20> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpTenantManagement::TenantName')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={tenantNameRef} |
||||
|
onChangeText={handleChange('name')} |
||||
|
onBlur={handleBlur('name')} |
||||
|
value={values.name} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.name}</ValidationMessage> |
||||
|
</Content> |
||||
|
</Container> |
||||
|
<FormButtons |
||||
|
submit={handleSubmit} |
||||
|
remove={remove} |
||||
|
removeMessage={i18n.t('AbpTenantManagement::TenantDeletionConfirmationMessage', { |
||||
|
0: editingTenant.name, |
||||
|
})} |
||||
|
isSubmitDisabled={!isValid} |
||||
|
isShowRemove={!!editingTenant.id && hasRemovePermission} |
||||
|
/> |
||||
|
</> |
||||
|
)} |
||||
|
</Formik> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
CreateUpdateTenantForm.propTypes = { |
||||
|
editingTenant: PropTypes.object, |
||||
|
submit: PropTypes.func.isRequired, |
||||
|
remove: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
marginBottom: 50, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default CreateUpdateTenantForm; |
||||
@ -0,0 +1,77 @@ |
|||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useState, useCallback } from 'react'; |
||||
|
import { useFocusEffect } from '@react-navigation/native'; |
||||
|
import { |
||||
|
createTenant, |
||||
|
getTenantById, |
||||
|
removeTenant, |
||||
|
updateTenant, |
||||
|
} from '../../api/TenantManagementAPI'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import { createLoadingSelector } from '../../store/selectors/LoadingSelectors'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import CreateUpdateTenantForm from './CreateUpdateTenantForm'; |
||||
|
|
||||
|
function CreateUpdateTenantScreen({ navigation, route, startLoading, stopLoading }) { |
||||
|
const [tenant, setTenant] = useState(); |
||||
|
const tenantId = route.params?.tenantId; |
||||
|
|
||||
|
const remove = () => { |
||||
|
startLoading({ key: 'removeTenant' }); |
||||
|
removeTenant(tenantId) |
||||
|
.then(() => navigation.goBack()) |
||||
|
.finally(() => stopLoading({ key: 'removeTenant' })); |
||||
|
}; |
||||
|
|
||||
|
useFocusEffect( |
||||
|
useCallback(() => { |
||||
|
if (tenantId) { |
||||
|
getTenantById(tenantId).then((data = {}) => setTenant(data)); |
||||
|
} |
||||
|
}, []), |
||||
|
); |
||||
|
|
||||
|
const submit = data => { |
||||
|
startLoading({ key: 'saveTenant' }); |
||||
|
let request; |
||||
|
if (data.id) { |
||||
|
request = updateTenant(data, tenantId); |
||||
|
} else { |
||||
|
request = createTenant(data); |
||||
|
} |
||||
|
|
||||
|
request |
||||
|
.then(() => { |
||||
|
navigation.goBack(); |
||||
|
}) |
||||
|
.finally(() => stopLoading({ key: 'saveTenant' })); |
||||
|
}; |
||||
|
|
||||
|
const renderForm = () => ( |
||||
|
<CreateUpdateTenantForm editingTenant={tenant} submit={submit} remove={remove} /> |
||||
|
); |
||||
|
|
||||
|
if (tenantId && tenant) { |
||||
|
return renderForm(); |
||||
|
} |
||||
|
|
||||
|
if (!tenantId) { |
||||
|
return renderForm(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
CreateUpdateTenantScreen.propTypes = { |
||||
|
startLoading: PropTypes.func.isRequired, |
||||
|
stopLoading: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: CreateUpdateTenantScreen, |
||||
|
stateProps: state => ({ loading: createLoadingSelector()(state) }), |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,242 @@ |
|||||
|
import { Formik } from 'formik'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { |
||||
|
Body, |
||||
|
Button, |
||||
|
CheckBox, |
||||
|
Container, |
||||
|
Content, |
||||
|
Input, |
||||
|
InputGroup, |
||||
|
Item, |
||||
|
Icon, |
||||
|
Label, |
||||
|
ListItem, |
||||
|
Segment, |
||||
|
Text, |
||||
|
} from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useRef, useState } from 'react'; |
||||
|
import { StyleSheet, TouchableOpacity, View } from 'react-native'; |
||||
|
import * as Yup from 'yup'; |
||||
|
import FormButtons from '../../components/FormButtons/FormButtons'; |
||||
|
import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; |
||||
|
import { usePermission } from '../../hooks/UsePermission'; |
||||
|
import UserRoles from './UserRoles'; |
||||
|
|
||||
|
const validations = { |
||||
|
userName: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
email: Yup.string() |
||||
|
.email('AbpAccount::ThisFieldIsNotAValidEmailAddress.') |
||||
|
.required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
}; |
||||
|
|
||||
|
let roleNames = []; |
||||
|
|
||||
|
function onChangeRoles(roles) { |
||||
|
roleNames = roles; |
||||
|
} |
||||
|
|
||||
|
function CreateUpdateUserForm({ editingUser = {}, submit, remove }) { |
||||
|
const [selectedTab, setSelectedTab] = useState(0); |
||||
|
const [showPassword, setShowPassword] = useState(false); |
||||
|
|
||||
|
const usernameRef = useRef(); |
||||
|
const nameRef = useRef(); |
||||
|
const surnameRef = useRef(); |
||||
|
const emailRef = useRef(); |
||||
|
const phoneNumberRef = useRef(); |
||||
|
const passwordRef = useRef(); |
||||
|
|
||||
|
const hasRemovePermission = usePermission('AbpIdentity.Users.Delete'); |
||||
|
|
||||
|
const onSubmit = values => { |
||||
|
submit({ |
||||
|
...editingUser, |
||||
|
...values, |
||||
|
roleNames, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const passwordValidation = Yup.lazy(() => { |
||||
|
if (editingUser.id) { |
||||
|
return Yup.string(); |
||||
|
} |
||||
|
return Yup.string().required('AbpAccount::ThisFieldIsRequired.'); |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<Formik |
||||
|
enableReinitialize |
||||
|
validationSchema={Yup.object().shape({ |
||||
|
...validations, |
||||
|
password: passwordValidation, |
||||
|
})} |
||||
|
initialValues={{ |
||||
|
lockoutEnabled: false, |
||||
|
twoFactorEnabled: false, |
||||
|
...editingUser, |
||||
|
}} |
||||
|
onSubmit={values => onSubmit(values)}> |
||||
|
{({ handleChange, handleBlur, handleSubmit, values, errors, isValid, setFieldValue }) => ( |
||||
|
<> |
||||
|
<Segment> |
||||
|
<Button first primary={selectedTab === 0} onPress={() => setSelectedTab(0)}> |
||||
|
<Text dark light={selectedTab === 0}> |
||||
|
{i18n.t('AbpIdentity::UserInformations')} |
||||
|
</Text> |
||||
|
</Button> |
||||
|
<Button last primary={selectedTab === 1} onPress={() => setSelectedTab(1)}> |
||||
|
<Text dark light={selectedTab === 1}> |
||||
|
{i18n.t('AbpIdentity::Roles')} |
||||
|
</Text> |
||||
|
</Button> |
||||
|
</Segment> |
||||
|
<Container style={styles.container}> |
||||
|
<Content px20> |
||||
|
<View style={{ display: selectedTab === 0 ? 'flex' : 'none' }}> |
||||
|
<View style={{ alignItems: 'center', margin: 10, zIndex: 1 }} /> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::UserName')}*</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={usernameRef} |
||||
|
onSubmitEditing={() => nameRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('userName')} |
||||
|
onBlur={handleBlur('userName')} |
||||
|
value={values.userName} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.userName}</ValidationMessage> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:Name')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={nameRef} |
||||
|
onSubmitEditing={() => surnameRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('name')} |
||||
|
onBlur={handleBlur('name')} |
||||
|
value={values.name} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:Surname')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={surnameRef} |
||||
|
onSubmitEditing={() => emailRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('surname')} |
||||
|
onBlur={handleBlur('surname')} |
||||
|
value={values.surname} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::EmailAddress')}*</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={emailRef} |
||||
|
onSubmitEditing={() => phoneNumberRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('email')} |
||||
|
onBlur={handleBlur('email')} |
||||
|
value={values.email} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.email}</ValidationMessage> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::PhoneNumber')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={phoneNumberRef} |
||||
|
returnKeyType={!editingUser.id ? 'next' : 'default'} |
||||
|
onChangeText={handleChange('phoneNumber')} |
||||
|
onBlur={handleBlur('phoneNumber')} |
||||
|
value={values.phoneNumber} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
{!editingUser.id ? ( |
||||
|
<> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::Password')}*</Label> |
||||
|
<Item abpInput> |
||||
|
<Input |
||||
|
ref={passwordRef} |
||||
|
secureTextEntry={!showPassword} |
||||
|
onChangeText={handleChange('password')} |
||||
|
onBlur={handleBlur('password')} |
||||
|
value={values.password} |
||||
|
/> |
||||
|
<Icon |
||||
|
name={showPassword ? 'eye-off' : 'eye'} |
||||
|
onPress={() => setShowPassword(!showPassword)} |
||||
|
/> |
||||
|
</Item> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.password}</ValidationMessage> |
||||
|
</> |
||||
|
) : null} |
||||
|
<ListItem> |
||||
|
<CheckBox |
||||
|
checked={values.lockoutEnabled} |
||||
|
onPress={() => setFieldValue('lockoutEnabled', !values.lockoutEnabled)} |
||||
|
/> |
||||
|
<Body> |
||||
|
<TouchableOpacity |
||||
|
onPress={() => setFieldValue('lockoutEnabled', !values.lockoutEnabled)}> |
||||
|
<Text>{i18n.t('AbpIdentity::DisplayName:LockoutEnabled')}</Text> |
||||
|
</TouchableOpacity> |
||||
|
</Body> |
||||
|
</ListItem> |
||||
|
<ListItem> |
||||
|
<CheckBox |
||||
|
checked={values.twoFactorEnabled} |
||||
|
onPress={() => setFieldValue('twoFactorEnabled', !values.twoFactorEnabled)} |
||||
|
/> |
||||
|
<Body> |
||||
|
<TouchableOpacity |
||||
|
onPress={() => setFieldValue('twoFactorEnabled', !values.twoFactorEnabled)}> |
||||
|
<Text>{i18n.t('AbpIdentity::DisplayName:TwoFactorEnabled')}</Text> |
||||
|
</TouchableOpacity> |
||||
|
</Body> |
||||
|
</ListItem> |
||||
|
</View> |
||||
|
<View |
||||
|
style={{ |
||||
|
display: selectedTab === 1 ? 'flex' : 'none', |
||||
|
flex: 1, |
||||
|
}}> |
||||
|
<UserRoles {...{ editingUser, onChangeRoles }} /> |
||||
|
</View> |
||||
|
</Content> |
||||
|
</Container> |
||||
|
<FormButtons |
||||
|
submit={handleSubmit} |
||||
|
remove={remove} |
||||
|
removeMessage={i18n.t('AbpIdentity::UserDeletionConfirmationMessage', { |
||||
|
0: editingUser.userName, |
||||
|
})} |
||||
|
isSubmitDisabled={!isValid} |
||||
|
isShowRemove={!!editingUser.id && hasRemovePermission} |
||||
|
/> |
||||
|
</> |
||||
|
)} |
||||
|
</Formik> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
CreateUpdateUserForm.propTypes = { |
||||
|
editingUser: PropTypes.object, |
||||
|
submit: PropTypes.func.isRequired, |
||||
|
remove: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
marginBottom: 50, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default CreateUpdateUserForm; |
||||
@ -0,0 +1,69 @@ |
|||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
import { createUser, getUserById, removeUser, updateUser } from '../../api/IdentityAPI'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import { createLoadingSelector } from '../../store/selectors/LoadingSelectors'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import CreateUpdateUserForm from './CreateUpdateUserForm'; |
||||
|
|
||||
|
function CreateUpdateUserScreen({ navigation, route, startLoading, stopLoading }) { |
||||
|
const [user, setUser] = useState(); |
||||
|
const userId = route.params?.userId; |
||||
|
|
||||
|
const remove = () => { |
||||
|
startLoading({ key: 'remove user' }); |
||||
|
removeUser(userId) |
||||
|
.then(() => navigation.goBack()) |
||||
|
.finally(() => stopLoading({ key: 'remove user' })); |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (userId) { |
||||
|
getUserById(userId).then((data = {}) => setUser(data)); |
||||
|
} |
||||
|
}, []); |
||||
|
|
||||
|
const submit = data => { |
||||
|
startLoading({ key: 'saveUser' }); |
||||
|
let request; |
||||
|
if (data.id) { |
||||
|
request = updateUser(data, userId); |
||||
|
} else { |
||||
|
request = createUser(data); |
||||
|
} |
||||
|
|
||||
|
request |
||||
|
.then(() => { |
||||
|
navigation.goBack(); |
||||
|
}) |
||||
|
.finally(() => stopLoading({ key: 'saveUser' })); |
||||
|
}; |
||||
|
|
||||
|
const renderForm = () => ( |
||||
|
<CreateUpdateUserForm editingUser={user} submit={submit} remove={remove} /> |
||||
|
); |
||||
|
|
||||
|
if (userId && user) { |
||||
|
return renderForm(); |
||||
|
} |
||||
|
|
||||
|
if (!userId) { |
||||
|
return renderForm(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
CreateUpdateUserScreen.propTypes = { |
||||
|
startLoading: PropTypes.func.isRequired, |
||||
|
stopLoading: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: CreateUpdateUserScreen, |
||||
|
stateProps: state => ({ loading: createLoadingSelector()(state) }), |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,60 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { List, ListItem, CheckBox, Body, Text } from 'native-base'; |
||||
|
import { TouchableOpacity } from 'react-native'; |
||||
|
import { getAllRoles, getUserRoles } from '../../api/IdentityAPI'; |
||||
|
|
||||
|
function UserRoles({ editingUser = {}, onChangeRoles }) { |
||||
|
const [roles, setRoles] = useState([]); |
||||
|
|
||||
|
const onPress = index => { |
||||
|
setRoles( |
||||
|
roles.map((role, i) => ({ |
||||
|
...role, |
||||
|
isSelected: index === i ? !role.isSelected : role.isSelected, |
||||
|
})), |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const requests = [getAllRoles()]; |
||||
|
if (editingUser.id) requests.push(getUserRoles(editingUser.id)); |
||||
|
|
||||
|
Promise.all(requests).then(([allRoles = [], userRoles = []]) => { |
||||
|
setRoles( |
||||
|
allRoles.map(role => ({ |
||||
|
...role, |
||||
|
isSelected: editingUser.id |
||||
|
? !!userRoles?.find(userRole => userRole?.id === role?.id) |
||||
|
: role.isDefault, |
||||
|
})), |
||||
|
); |
||||
|
}); |
||||
|
}, []); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
onChangeRoles(roles.filter(role => role.isSelected).map(role => role.name)); |
||||
|
}, [roles]); |
||||
|
|
||||
|
return ( |
||||
|
<List> |
||||
|
{roles.map((role, index) => ( |
||||
|
<ListItem key={role.id}> |
||||
|
<CheckBox checked={role.isSelected} onPress={() => onPress(index)} /> |
||||
|
<Body> |
||||
|
<TouchableOpacity onPress={() => onPress(index)}> |
||||
|
<Text>{role.name}</Text> |
||||
|
</TouchableOpacity> |
||||
|
</Body> |
||||
|
</ListItem> |
||||
|
))} |
||||
|
</List> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
UserRoles.propTypes = { |
||||
|
editingUser: PropTypes.objectOf(PropTypes.any).isRequired, |
||||
|
onChangeRoles: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default UserRoles; |
||||
@ -0,0 +1,34 @@ |
|||||
|
import i18n from 'i18n-js'; |
||||
|
import { Text } from 'native-base'; |
||||
|
import React from 'react'; |
||||
|
import { StyleSheet, View } from 'react-native'; |
||||
|
|
||||
|
function HomeScreen() { |
||||
|
return ( |
||||
|
<View style={styles.container}> |
||||
|
<Text style={styles.title}> {i18n.t('::Welcome')}</Text> |
||||
|
<Text style={styles.centeredText}> {i18n.t('::LongWelcomeMessage')}</Text> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
flex: 1, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
paddingHorizontal: 20, |
||||
|
backgroundColor: '#fff', |
||||
|
}, |
||||
|
centeredText: { |
||||
|
textAlign: 'center', |
||||
|
}, |
||||
|
title: { |
||||
|
marginBottom: 30, |
||||
|
fontSize: 32, |
||||
|
fontWeight: '300', |
||||
|
textAlign: 'center', |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default HomeScreen; |
||||
@ -0,0 +1,128 @@ |
|||||
|
import { Formik } from 'formik'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { |
||||
|
Button, |
||||
|
Container, |
||||
|
Content, |
||||
|
Form, |
||||
|
Input, |
||||
|
InputGroup, |
||||
|
Item, |
||||
|
Label, |
||||
|
Text, |
||||
|
Icon, |
||||
|
} from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useState } from 'react'; |
||||
|
import { View } from 'react-native'; |
||||
|
import * as Yup from 'yup'; |
||||
|
import { login } from '../../api/AccountAPI'; |
||||
|
import TenantBox from '../../components/TenantBox/TenantBox'; |
||||
|
import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; |
||||
|
import AppActions from '../../store/actions/AppActions'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import PersistentStorageActions from '../../store/actions/PersistentStorageActions'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
|
||||
|
const ValidationSchema = Yup.object().shape({ |
||||
|
username: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
password: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
}); |
||||
|
|
||||
|
function LoginScreen({ startLoading, stopLoading, setToken, fetchAppConfig }) { |
||||
|
const [showTenantSelection, setShowTenantSelection] = useState(false); |
||||
|
const [showPassword, setShowPassword] = useState(false); |
||||
|
|
||||
|
const toggleTenantSelection = () => { |
||||
|
setShowTenantSelection(!showTenantSelection); |
||||
|
}; |
||||
|
|
||||
|
const submit = ({ username, password }) => { |
||||
|
startLoading({ key: 'login' }); |
||||
|
login({ username, password }) |
||||
|
.then(data => |
||||
|
setToken({ |
||||
|
...data, |
||||
|
expire_time: new Date().valueOf() + data.expires_in, |
||||
|
scope: undefined, |
||||
|
}), |
||||
|
) |
||||
|
.then( |
||||
|
() => |
||||
|
new Promise(resolve => |
||||
|
fetchAppConfig({ showLoading: false, callback: () => resolve(true) }), |
||||
|
), |
||||
|
) |
||||
|
.finally(() => stopLoading({ key: 'login' })); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Container> |
||||
|
<TenantBox |
||||
|
showTenantSelection={showTenantSelection} |
||||
|
toggleTenantSelection={toggleTenantSelection} |
||||
|
/> |
||||
|
{!showTenantSelection ? ( |
||||
|
<Content px20 style={{ flex: 1 }}> |
||||
|
<Formik |
||||
|
validationSchema={ValidationSchema} |
||||
|
initialValues={{ username: '', password: '' }} |
||||
|
onSubmit={submit}> |
||||
|
{({ handleChange, handleBlur, handleSubmit, values, errors }) => ( |
||||
|
<Form> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpAccount::UserNameOrEmailAddress')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
onChangeText={handleChange('username')} |
||||
|
onBlur={handleBlur('username')} |
||||
|
value={values.username} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.username}</ValidationMessage> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpAccount::Password')}</Label> |
||||
|
<Item abpInput> |
||||
|
<Input |
||||
|
secureTextEntry={!showPassword} |
||||
|
onChangeText={handleChange('password')} |
||||
|
onBlur={handleBlur('password')} |
||||
|
value={values.password} |
||||
|
/> |
||||
|
<Icon |
||||
|
name={showPassword ? 'eye-off' : 'eye'} |
||||
|
onPress={() => setShowPassword(!showPassword)} |
||||
|
/> |
||||
|
</Item> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.password}</ValidationMessage> |
||||
|
<View style={{ marginTop: 20, alignItems: 'center' }}> |
||||
|
<Button abpButton onPress={handleSubmit}> |
||||
|
<Text>{i18n.t('AbpAccount::Login')}</Text> |
||||
|
</Button> |
||||
|
</View> |
||||
|
</Form> |
||||
|
)} |
||||
|
</Formik> |
||||
|
</Content> |
||||
|
) : null} |
||||
|
</Container> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
LoginScreen.propTypes = { |
||||
|
startLoading: PropTypes.func.isRequired, |
||||
|
stopLoading: PropTypes.func.isRequired, |
||||
|
setToken: PropTypes.func.isRequired, |
||||
|
fetchAppConfig: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: LoginScreen, |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
fetchAppConfig: AppActions.fetchAppConfigAsync, |
||||
|
setToken: PersistentStorageActions.setToken, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,122 @@ |
|||||
|
import { Formik } from 'formik'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { Container, Content, Form, Input, InputGroup, Label } from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useRef } from 'react'; |
||||
|
import * as Yup from 'yup'; |
||||
|
import FormButtons from '../../components/FormButtons/FormButtons'; |
||||
|
import ValidationMessage from '../../components/ValidationMessage/ValidationMessage'; |
||||
|
|
||||
|
const ValidationSchema = Yup.object().shape({ |
||||
|
userName: Yup.string().required('AbpAccount::ThisFieldIsRequired.'), |
||||
|
email: Yup.string() |
||||
|
.required('AbpAccount::ThisFieldIsRequired.') |
||||
|
.email('AbpAccount::ThisFieldIsNotAValidEmailAddress.'), |
||||
|
}); |
||||
|
|
||||
|
function ManageProfileForm({ editingUser = {}, submit, cancel }) { |
||||
|
const usernameRef = useRef(); |
||||
|
const nameRef = useRef(); |
||||
|
const surnameRef = useRef(); |
||||
|
const emailRef = useRef(); |
||||
|
const phoneNumberRef = useRef(); |
||||
|
|
||||
|
const onSubmit = values => { |
||||
|
submit({ |
||||
|
...editingUser, |
||||
|
...values, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Formik |
||||
|
enableReinitialize |
||||
|
validationSchema={ValidationSchema} |
||||
|
initialValues={{ |
||||
|
...editingUser, |
||||
|
}} |
||||
|
onSubmit={values => onSubmit(values)}> |
||||
|
{({ handleChange, handleBlur, handleSubmit, values, errors, isValid }) => ( |
||||
|
<> |
||||
|
<Container> |
||||
|
<Content px20> |
||||
|
<Form> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::UserName')}*</Label> |
||||
|
<Input |
||||
|
ref={usernameRef} |
||||
|
onSubmitEditing={() => nameRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('userName')} |
||||
|
onBlur={handleBlur('userName')} |
||||
|
value={values.userName} |
||||
|
abpInput |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.userName}</ValidationMessage> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:Name')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={nameRef} |
||||
|
onSubmitEditing={() => surnameRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('name')} |
||||
|
onBlur={handleBlur('name')} |
||||
|
value={values.name} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::DisplayName:Surname')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={surnameRef} |
||||
|
onSubmitEditing={() => phoneNumberRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('surname')} |
||||
|
onBlur={handleBlur('surname')} |
||||
|
value={values.surname} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::PhoneNumber')}</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={phoneNumberRef} |
||||
|
onSubmitEditing={() => emailRef.current._root.focus()} |
||||
|
returnKeyType="next" |
||||
|
onChangeText={handleChange('phoneNumber')} |
||||
|
onBlur={handleBlur('phoneNumber')} |
||||
|
value={values.phoneNumber} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<InputGroup abpInputGroup> |
||||
|
<Label abpLabel>{i18n.t('AbpIdentity::EmailAddress')}*</Label> |
||||
|
<Input |
||||
|
abpInput |
||||
|
ref={emailRef} |
||||
|
returnKeyType="done" |
||||
|
onSubmitEditing={handleSubmit} |
||||
|
onChangeText={handleChange('email')} |
||||
|
onBlur={handleBlur('email')} |
||||
|
value={values.email} |
||||
|
/> |
||||
|
</InputGroup> |
||||
|
<ValidationMessage>{errors.email}</ValidationMessage> |
||||
|
</Form> |
||||
|
</Content> |
||||
|
</Container> |
||||
|
<FormButtons submit={handleSubmit} cancel={cancel} isSubmitDisabled={!isValid} /> |
||||
|
</> |
||||
|
)} |
||||
|
</Formik> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
ManageProfileForm.propTypes = { |
||||
|
editingUser: PropTypes.object.isRequired, |
||||
|
submit: PropTypes.func.isRequired, |
||||
|
cancel: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default ManageProfileForm; |
||||
@ -0,0 +1,50 @@ |
|||||
|
import React, { useState, useEffect } from 'react'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import { updateProfileDetail, getProfileDetail } from '../../api/IdentityAPI'; |
||||
|
import ManageProfileForm from './ManageProfileForm'; |
||||
|
import LoadingActions from '../../store/actions/LoadingActions'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
|
||||
|
function ManageProfileScreen({ navigation, startLoading, stopLoading }) { |
||||
|
const [user, setUser] = useState(); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (!user) { |
||||
|
startLoading({ key: 'manageProfile' }); |
||||
|
getProfileDetail() |
||||
|
.then((data = {}) => setUser(data)) |
||||
|
.finally(() => stopLoading({ key: 'manageProfile' })); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const submit = data => { |
||||
|
startLoading({ key: 'manageProfile' }); |
||||
|
|
||||
|
updateProfileDetail(data) |
||||
|
.then(() => { |
||||
|
navigation.goBack(); |
||||
|
}) |
||||
|
.finally(() => stopLoading({ key: 'manageProfile' })); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
{user ? ( |
||||
|
<ManageProfileForm editingUser={user} submit={submit} cancel={() => navigation.goBack()} /> |
||||
|
) : null} |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
ManageProfileScreen.propTypes = { |
||||
|
startLoading: PropTypes.func.isRequired, |
||||
|
stopLoading: PropTypes.func.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: ManageProfileScreen, |
||||
|
dispatchProps: { |
||||
|
startLoading: LoadingActions.start, |
||||
|
stopLoading: LoadingActions.stop, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,145 @@ |
|||||
|
import i18n from 'i18n-js'; |
||||
|
import { |
||||
|
Body, |
||||
|
Button, |
||||
|
Icon, |
||||
|
Label, |
||||
|
Left, |
||||
|
List, |
||||
|
ListItem, |
||||
|
Picker, |
||||
|
Right, |
||||
|
Text, |
||||
|
Thumbnail, |
||||
|
} from 'native-base'; |
||||
|
import PropTypes from 'prop-types'; |
||||
|
import React, { useCallback, useState } from 'react'; |
||||
|
import { View } from 'react-native'; |
||||
|
import { useFocusEffect } from '@react-navigation/native'; |
||||
|
import { getProfileDetail } from '../../api/IdentityAPI'; |
||||
|
import AppActions from '../../store/actions/AppActions'; |
||||
|
import { |
||||
|
createLanguageSelector, |
||||
|
createLanguagesSelector, |
||||
|
} from '../../store/selectors/AppSelectors'; |
||||
|
import { connectToRedux } from '../../utils/ReduxConnect'; |
||||
|
import { createTenantSelector } from '../../store/selectors/PersistentStorageSelectors'; |
||||
|
|
||||
|
function SettingsScreen({ |
||||
|
navigation, |
||||
|
language, |
||||
|
languages, |
||||
|
setLanguageAsync, |
||||
|
logoutAsync, |
||||
|
tenant = {}, |
||||
|
}) { |
||||
|
const [user, setUser] = useState({}); |
||||
|
|
||||
|
const fetchUser = () => { |
||||
|
getProfileDetail().then(data => { |
||||
|
setUser(data || {}); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
useFocusEffect( |
||||
|
useCallback(() => { |
||||
|
fetchUser(); |
||||
|
}, []), |
||||
|
); |
||||
|
|
||||
|
return ( |
||||
|
<View> |
||||
|
<List> |
||||
|
<ListItem itemDivider /> |
||||
|
<ListItem |
||||
|
noIndent |
||||
|
style={{ backgroundColor: '#fff' }} |
||||
|
onPress={() => navigation.navigate('ManageProfile')}> |
||||
|
<Left style={{ alignItems: 'center' }}> |
||||
|
<Thumbnail source={require('../../../assets/avatar.png')} /> |
||||
|
<Body> |
||||
|
<Text> |
||||
|
{tenant.name ? `${tenant.name} / ` : ''} |
||||
|
{user.userName ? `${user.userName}` : ''} |
||||
|
</Text> |
||||
|
<Text note>{user.email}</Text> |
||||
|
</Body> |
||||
|
</Left> |
||||
|
|
||||
|
<Right> |
||||
|
<Icon active name="arrow-forward" /> |
||||
|
</Right> |
||||
|
</ListItem> |
||||
|
<ListItem itemDivider /> |
||||
|
<ListItem |
||||
|
noIndent |
||||
|
icon |
||||
|
style={{ backgroundColor: '#fff' }} |
||||
|
onPress={() => navigation.navigate('ChangePassword')}> |
||||
|
<Body> |
||||
|
<Text>{i18n.t('AbpUi::ChangePassword')}</Text> |
||||
|
</Body> |
||||
|
<Right> |
||||
|
<Icon active name="arrow-forward" /> |
||||
|
</Right> |
||||
|
</ListItem> |
||||
|
<ListItem itemDivider /> |
||||
|
<ListItem itemDivider> |
||||
|
<Label abpLabel style={{ marginBottom: 0 }}> |
||||
|
{i18n.t('AbpUi::Language')} |
||||
|
</Label> |
||||
|
</ListItem> |
||||
|
<ListItem noIndent icon style={{ backgroundColor: '#fff' }}> |
||||
|
<Body> |
||||
|
<Picker |
||||
|
mode="dropdown" |
||||
|
iosHeader={i18n.t('AbpUi::Language')} |
||||
|
iosIcon={<Icon active name="arrow-down" />} |
||||
|
onValueChange={value => setLanguageAsync(value)} |
||||
|
selectedValue={language.cultureName} |
||||
|
textStyle={{ paddingLeft: 0 }}> |
||||
|
{languages.map(lang => ( |
||||
|
<Picker.Item |
||||
|
label={lang.displayName} |
||||
|
value={lang.cultureName} |
||||
|
key={lang.cultureName} |
||||
|
/> |
||||
|
))} |
||||
|
</Picker> |
||||
|
</Body> |
||||
|
</ListItem> |
||||
|
<ListItem itemDivider /> |
||||
|
<Button |
||||
|
abpButton |
||||
|
danger |
||||
|
style={{ borderRadius: 0 }} |
||||
|
onPress={() => { |
||||
|
logoutAsync(); |
||||
|
}}> |
||||
|
<Text>{i18n.t('AbpAccount::Logout')}</Text> |
||||
|
</Button> |
||||
|
</List> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
SettingsScreen.propTypes = { |
||||
|
setLanguageAsync: PropTypes.func.isRequired, |
||||
|
logoutAsync: PropTypes.func.isRequired, |
||||
|
language: PropTypes.object.isRequired, |
||||
|
languages: PropTypes.array.isRequired, |
||||
|
tenant: PropTypes.object.isRequired, |
||||
|
}; |
||||
|
|
||||
|
export default connectToRedux({ |
||||
|
component: SettingsScreen, |
||||
|
stateProps: state => ({ |
||||
|
languages: createLanguagesSelector()(state), |
||||
|
language: createLanguageSelector()(state), |
||||
|
tenant: createTenantSelector()(state), |
||||
|
}), |
||||
|
dispatchProps: { |
||||
|
setLanguageAsync: AppActions.setLanguageAsync, |
||||
|
logoutAsync: AppActions.logoutAsync, |
||||
|
}, |
||||
|
}); |
||||
@ -0,0 +1,59 @@ |
|||||
|
import React from 'react'; |
||||
|
import i18n from 'i18n-js'; |
||||
|
import { ListItem, Text, Icon, Left, Body, Fab } from 'native-base'; |
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
import { getTenants } from '../../api/TenantManagementAPI'; |
||||
|
import { activeTheme } from '../../theme/variables'; |
||||
|
import DataList from '../../components/DataList/DataList'; |
||||
|
import { withPermission } from '../../hocs/PermissionHOC'; |
||||
|
|
||||
|
const CreateTenantButtonWithPermission = withPermission(Fab, 'AbpTenantManagement.Tenants.Create'); |
||||
|
|
||||
|
function TenantsScreen({ navigation }) { |
||||
|
return ( |
||||
|
<> |
||||
|
<DataList |
||||
|
navigation={navigation} |
||||
|
fetchFn={getTenants} |
||||
|
render={tenant => ( |
||||
|
<ListItem avatar onPress={() => navigateToCreateUpdateTenantScreen(navigation, tenant)}> |
||||
|
<Left style={styles.listItem}> |
||||
|
<Body> |
||||
|
<Text>{tenant.name}</Text> |
||||
|
</Body> |
||||
|
</Left> |
||||
|
</ListItem> |
||||
|
)} |
||||
|
/> |
||||
|
|
||||
|
<CreateTenantButtonWithPermission |
||||
|
style={{ backgroundColor: activeTheme.brandPrimary }} |
||||
|
position="bottomRight" |
||||
|
onPress={() => navigation.navigate('CreateUpdateTenant')}> |
||||
|
<Icon name="add" /> |
||||
|
</CreateTenantButtonWithPermission> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const navigateToCreateUpdateTenantScreen = (navigation, tenant = {}) => { |
||||
|
navigation.navigate('CreateUpdateTenant', { |
||||
|
tenantId: tenant.id, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
listItem: { |
||||
|
alignItems: 'center', |
||||
|
marginVertical: 3, |
||||
|
marginLeft: -15, |
||||
|
paddingTop: 5, |
||||
|
paddingBottom: 5, |
||||
|
paddingLeft: 15, |
||||
|
backgroundColor: '#f9f9f9', |
||||
|
width: '110%', |
||||
|
minHeight: 50, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default TenantsScreen; |
||||
@ -0,0 +1,61 @@ |
|||||
|
import React from 'react'; |
||||
|
import { ListItem, Text, Icon, Left, Thumbnail, Body, Fab } from 'native-base'; |
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
import { getUsers } from '../../api/IdentityAPI'; |
||||
|
import { activeTheme } from '../../theme/variables'; |
||||
|
import DataList from '../../components/DataList/DataList'; |
||||
|
import { withPermission } from '../../hocs/PermissionHOC'; |
||||
|
|
||||
|
const CreateUserButtonWithPermission = withPermission(Fab, 'AbpIdentity.Users.Create'); |
||||
|
|
||||
|
function UsersScreen({ navigation }) { |
||||
|
return ( |
||||
|
<> |
||||
|
<DataList |
||||
|
navigation={navigation} |
||||
|
fetchFn={getUsers} |
||||
|
render={user => ( |
||||
|
<ListItem avatar onPress={() => navigateToCreateUpdateUserScreen(navigation, user)}> |
||||
|
<Left style={styles.listItem}> |
||||
|
<Thumbnail source={require('../../../assets/avatar.png')} /> |
||||
|
<Body> |
||||
|
<Text>{user.userName}</Text> |
||||
|
<Text note>{user.email}</Text> |
||||
|
</Body> |
||||
|
</Left> |
||||
|
</ListItem> |
||||
|
)} |
||||
|
/> |
||||
|
|
||||
|
<CreateUserButtonWithPermission |
||||
|
containerStyle={{}} |
||||
|
style={{ backgroundColor: activeTheme.brandPrimary }} |
||||
|
position="bottomRight" |
||||
|
onPress={() => navigation.navigate('CreateUpdateUser')}> |
||||
|
<Icon name="add" /> |
||||
|
</CreateUserButtonWithPermission> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const navigateToCreateUpdateUserScreen = (navigation, user = {}) => { |
||||
|
navigation.navigate('CreateUpdateUser', { |
||||
|
userId: user.id, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { flex: 1 }, |
||||
|
listItem: { |
||||
|
alignItems: 'center', |
||||
|
marginVertical: 3, |
||||
|
marginLeft: -15, |
||||
|
paddingTop: 5, |
||||
|
paddingBottom: 5, |
||||
|
paddingLeft: 15, |
||||
|
backgroundColor: '#f9f9f9', |
||||
|
width: '110%', |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default UsersScreen; |
||||
@ -0,0 +1,19 @@ |
|||||
|
import { createAction } from '@reduxjs/toolkit'; |
||||
|
|
||||
|
const fetchAppConfigAsync = createAction( |
||||
|
'app/fetchAppConfigAsync', |
||||
|
({ callback = () => {}, showLoading = true } = {}) => ({ payload: { callback, showLoading } }), |
||||
|
); |
||||
|
|
||||
|
const setAppConfig = createAction('app/setAppConfig'); |
||||
|
|
||||
|
const setLanguageAsync = createAction('app/setLanguageAsync'); |
||||
|
|
||||
|
const logoutAsync = createAction('app/logoutAsync'); |
||||
|
|
||||
|
export default { |
||||
|
fetchAppConfigAsync, |
||||
|
setAppConfig, |
||||
|
setLanguageAsync, |
||||
|
logoutAsync, |
||||
|
}; |
||||
@ -0,0 +1,13 @@ |
|||||
|
import { createAction } from '@reduxjs/toolkit'; |
||||
|
|
||||
|
const start = createAction('loading/start'); |
||||
|
|
||||
|
const stop = createAction('loading/stop'); |
||||
|
|
||||
|
const clear = createAction('loading/clear'); |
||||
|
|
||||
|
export default { |
||||
|
start, |
||||
|
stop, |
||||
|
clear, |
||||
|
}; |
||||
@ -0,0 +1,13 @@ |
|||||
|
import { createAction } from '@reduxjs/toolkit'; |
||||
|
|
||||
|
const setToken = createAction('persistentStorage/setToken'); |
||||
|
|
||||
|
const setLanguage = createAction('persistentStorage/setLanguage'); |
||||
|
|
||||
|
const setTenant = createAction('persistentStorage/setTenant'); |
||||
|
|
||||
|
export default { |
||||
|
setToken, |
||||
|
setLanguage, |
||||
|
setTenant, |
||||
|
}; |
||||
@ -0,0 +1,25 @@ |
|||||
|
import { configureStore } from '@reduxjs/toolkit'; |
||||
|
import createSagaMiddleware from 'redux-saga'; |
||||
|
import { persistStore, persistReducer } from 'redux-persist'; |
||||
|
import { AsyncStorage } from 'react-native'; |
||||
|
import { rootSaga } from './sagas'; |
||||
|
import rootReducer from './reducers'; |
||||
|
|
||||
|
const sagaMiddleware = createSagaMiddleware(); |
||||
|
|
||||
|
const persistConfig = { |
||||
|
key: 'root', |
||||
|
storage: AsyncStorage, |
||||
|
whitelist: ['persistentStorage'], |
||||
|
}; |
||||
|
|
||||
|
const persistedReducer = persistReducer(persistConfig, rootReducer); |
||||
|
|
||||
|
export const store = configureStore({ |
||||
|
reducer: persistedReducer, |
||||
|
middleware: [sagaMiddleware], |
||||
|
}); |
||||
|
|
||||
|
export const persistor = persistStore(store); |
||||
|
|
||||
|
sagaMiddleware.run(rootSaga); |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { createReducer } from '@reduxjs/toolkit'; |
||||
|
import AppActions from '../actions/AppActions'; |
||||
|
|
||||
|
const initialState = { |
||||
|
appConfig: {}, |
||||
|
}; |
||||
|
|
||||
|
export default createReducer(initialState, builder => |
||||
|
builder.addCase(AppActions.setAppConfig, (state, action) => { |
||||
|
state.appConfig = action.payload; |
||||
|
}), |
||||
|
); |
||||
@ -0,0 +1,23 @@ |
|||||
|
import { createReducer } from '@reduxjs/toolkit'; |
||||
|
import LoadingActions from '../actions/LoadingActions'; |
||||
|
|
||||
|
const initialState = { activeLoadings: {}, loading: false }; |
||||
|
|
||||
|
export default createReducer(initialState, builder => |
||||
|
builder |
||||
|
.addCase(LoadingActions.start, (state, action) => { |
||||
|
const { key, opacity } = action.payload; |
||||
|
return { |
||||
|
...state, |
||||
|
actives: { ...state.activeLoadings, [key]: action }, |
||||
|
loading: true, |
||||
|
opacity, |
||||
|
}; |
||||
|
}) |
||||
|
.addCase(LoadingActions.stop, (state, action) => { |
||||
|
delete state.activeLoadings[action.payload.key]; |
||||
|
|
||||
|
state.loading = !!Object.keys(state.activeLoadings).length; |
||||
|
}) |
||||
|
.addCase(LoadingActions.clear, () => ({})), |
||||
|
); |
||||
@ -0,0 +1,17 @@ |
|||||
|
import { createReducer } from '@reduxjs/toolkit'; |
||||
|
import PersistentStorageActions from '../actions/PersistentStorageActions'; |
||||
|
|
||||
|
const initialState = { token: {}, language: null, tenant: {} }; |
||||
|
|
||||
|
export default createReducer(initialState, builder => |
||||
|
builder |
||||
|
.addCase(PersistentStorageActions.setToken, (state, action) => { |
||||
|
state.token = action.payload; |
||||
|
}) |
||||
|
.addCase(PersistentStorageActions.setLanguage, (state, action) => { |
||||
|
state.language = action.payload; |
||||
|
}) |
||||
|
.addCase(PersistentStorageActions.setTenant, (state, action) => { |
||||
|
state.tenant = action.payload; |
||||
|
}), |
||||
|
); |
||||
@ -0,0 +1,12 @@ |
|||||
|
import { combineReducers } from '@reduxjs/toolkit'; |
||||
|
import LoadingReducer from './LoadingReducer'; |
||||
|
import AppReducer from './AppReducer'; |
||||
|
import PersistentStorageReducer from './PersistentStorageReducer'; |
||||
|
|
||||
|
const rootReducer = combineReducers({ |
||||
|
loading: LoadingReducer, |
||||
|
app: AppReducer, |
||||
|
persistentStorage: PersistentStorageReducer, |
||||
|
}); |
||||
|
|
||||
|
export default rootReducer; |
||||
@ -0,0 +1,34 @@ |
|||||
|
import { call, put, takeLatest, all } from 'redux-saga/effects'; |
||||
|
import { getApplicationConfiguration } from '../../api/ApplicationConfigurationAPI'; |
||||
|
import { logout as logoutAsync } from '../../api/AccountAPI'; |
||||
|
import AppActions from '../actions/AppActions'; |
||||
|
import LoadingActions from '../actions/LoadingActions'; |
||||
|
import PersistentStorageActions from '../actions/PersistentStorageActions'; |
||||
|
|
||||
|
function* fetchAppConfig({ payload: { showLoading, callback } }) { |
||||
|
if (showLoading) yield put(LoadingActions.start({ key: 'appConfig', opacity: 1 })); |
||||
|
const data = yield call(getApplicationConfiguration); |
||||
|
yield put(AppActions.setAppConfig(data)); |
||||
|
yield put(PersistentStorageActions.setLanguage(data.localization.currentCulture.cultureName)); |
||||
|
if (showLoading) yield put(LoadingActions.stop({ key: 'appConfig' })); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function* setLanguage(action) { |
||||
|
yield put(PersistentStorageActions.setLanguage(action.payload)); |
||||
|
yield put(AppActions.fetchAppConfigAsync()); |
||||
|
} |
||||
|
|
||||
|
function* logout() { |
||||
|
yield call(logoutAsync); |
||||
|
yield put(PersistentStorageActions.setToken({})); |
||||
|
yield put(AppActions.fetchAppConfigAsync()); |
||||
|
} |
||||
|
|
||||
|
export default function*() { |
||||
|
yield all([ |
||||
|
takeLatest(AppActions.setLanguageAsync.type, setLanguage), |
||||
|
takeLatest(AppActions.fetchAppConfigAsync.type, fetchAppConfig), |
||||
|
takeLatest(AppActions.logoutAsync.type, logout), |
||||
|
]); |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
import { all, fork } from 'redux-saga/effects'; |
||||
|
import AppSaga from './AppSaga'; |
||||
|
|
||||
|
export function* rootSaga() { |
||||
|
yield all([fork(AppSaga)]); |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
import { createSelector } from 'reselect'; |
||||
|
|
||||
|
const getApp = state => state.app; |
||||
|
|
||||
|
export function createAppConfigSelector() { |
||||
|
return createSelector([getApp], state => state.appConfig); |
||||
|
} |
||||
|
|
||||
|
export function createLanguageSelector() { |
||||
|
return createSelector([getApp], state => state?.appConfig?.localization?.currentCulture); |
||||
|
} |
||||
|
|
||||
|
export function createLanguagesSelector() { |
||||
|
return createSelector([getApp], state => state?.appConfig?.localization?.languages); |
||||
|
} |
||||
|
|
||||
|
export function createGrantedPolicySelector(key) { |
||||
|
return createSelector([getApp], state => state?.appConfig?.auth?.grantedPolicies[key] ?? false); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
import { createSelector } from 'reselect'; |
||||
|
|
||||
|
const getLoading = state => state.loading; |
||||
|
|
||||
|
export function createLoadingSelector() { |
||||
|
return createSelector([getLoading], loading => loading.loading); |
||||
|
} |
||||
|
|
||||
|
export function createOpacitySelector() { |
||||
|
return createSelector([getLoading], loading => loading.opacity); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
import { createSelector } from 'reselect'; |
||||
|
|
||||
|
const getPersistentStorage = state => state.persistentStorage; |
||||
|
|
||||
|
export function createTokenSelector() { |
||||
|
return createSelector([getPersistentStorage], persistentStorage => persistentStorage.token); |
||||
|
} |
||||
|
|
||||
|
export function createTenantSelector() { |
||||
|
return createSelector([getPersistentStorage], persistentStorage => persistentStorage.tenant); |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const badgeTheme = { |
||||
|
'.primary': { |
||||
|
backgroundColor: variables.buttonPrimaryBg, |
||||
|
}, |
||||
|
'.warning': { |
||||
|
backgroundColor: variables.buttonWarningBg, |
||||
|
}, |
||||
|
'.info': { |
||||
|
backgroundColor: variables.buttonInfoBg, |
||||
|
}, |
||||
|
'.success': { |
||||
|
backgroundColor: variables.buttonSuccessBg, |
||||
|
}, |
||||
|
'.danger': { |
||||
|
backgroundColor: variables.buttonDangerBg, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.badgeColor, |
||||
|
fontSize: variables.fontSizeBase, |
||||
|
lineHeight: variables.lineHeight - 1, |
||||
|
textAlign: 'center', |
||||
|
paddingHorizontal: 3, |
||||
|
}, |
||||
|
backgroundColor: variables.badgeBg, |
||||
|
padding: variables.badgePadding, |
||||
|
paddingHorizontal: 6, |
||||
|
alignSelf: 'flex-start', |
||||
|
justifyContent: variables.platform === PLATFORM.IOS ? 'center' : undefined, |
||||
|
borderRadius: 13.5, |
||||
|
height: 27, |
||||
|
}; |
||||
|
return badgeTheme; |
||||
|
}; |
||||
@ -0,0 +1,11 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const bodyTheme = { |
||||
|
flex: 1, |
||||
|
alignItems: 'center', |
||||
|
alignSelf: 'center', |
||||
|
}; |
||||
|
|
||||
|
return bodyTheme; |
||||
|
}; |
||||
@ -0,0 +1,384 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platformStyle } = variables; |
||||
|
const { platform } = variables; |
||||
|
const darkCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.brandDark, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.brandDark, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.brandDark, |
||||
|
}, |
||||
|
}; |
||||
|
const lightCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.brandLight, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.brandLight, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.brandLight, |
||||
|
}, |
||||
|
}; |
||||
|
const primaryCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonPrimaryBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonPrimaryBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonPrimaryBg, |
||||
|
}, |
||||
|
}; |
||||
|
const successCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonSuccessBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonSuccessBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonSuccessBg, |
||||
|
}, |
||||
|
}; |
||||
|
const infoCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonInfoBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonInfoBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonInfoBg, |
||||
|
}, |
||||
|
}; |
||||
|
const warningCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonWarningBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonWarningBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonWarningBg, |
||||
|
}, |
||||
|
}; |
||||
|
const dangerCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonDangerBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonDangerBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonDangerBg, |
||||
|
}, |
||||
|
}; |
||||
|
const buttonTheme = { |
||||
|
'.disabled': { |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.brandLight, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.brandLight, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
'.bordered': { |
||||
|
'.dark': { |
||||
|
...darkCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.brandDark, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.light': { |
||||
|
...lightCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.brandLight, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.primary': { |
||||
|
...primaryCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonPrimaryBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.success': { |
||||
|
...successCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonSuccessBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.info': { |
||||
|
...infoCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonInfoBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.warning': { |
||||
|
...warningCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonWarningBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.danger': { |
||||
|
...dangerCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonDangerBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
}, |
||||
|
'.disabled': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonDisabledBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
}, |
||||
|
...primaryCommon, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
elevation: null, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowOpacity: null, |
||||
|
shadowRadius: null, |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
|
||||
|
'.dark': { |
||||
|
'.bordered': { |
||||
|
...darkCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.brandDark, |
||||
|
}, |
||||
|
'.light': { |
||||
|
'.transparent': { |
||||
|
...lightCommon, |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
'.bordered': { |
||||
|
...lightCommon, |
||||
|
}, |
||||
|
...darkCommon, |
||||
|
backgroundColor: variables.brandLight, |
||||
|
}, |
||||
|
|
||||
|
'.primary': { |
||||
|
'.bordered': { |
||||
|
...primaryCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonPrimaryBg, |
||||
|
}, |
||||
|
|
||||
|
'.success': { |
||||
|
'.bordered': { |
||||
|
...successCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonSuccessBg, |
||||
|
}, |
||||
|
|
||||
|
'.info': { |
||||
|
'.bordered': { |
||||
|
...infoCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonInfoBg, |
||||
|
}, |
||||
|
|
||||
|
'.warning': { |
||||
|
'.bordered': { |
||||
|
...warningCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonWarningBg, |
||||
|
}, |
||||
|
|
||||
|
'.danger': { |
||||
|
'.bordered': { |
||||
|
...dangerCommon, |
||||
|
}, |
||||
|
backgroundColor: variables.buttonDangerBg, |
||||
|
}, |
||||
|
|
||||
|
'.block': { |
||||
|
justifyContent: 'center', |
||||
|
alignSelf: 'stretch', |
||||
|
}, |
||||
|
|
||||
|
'.full': { |
||||
|
justifyContent: 'center', |
||||
|
alignSelf: 'stretch', |
||||
|
borderRadius: 0, |
||||
|
}, |
||||
|
|
||||
|
'.rounded': { |
||||
|
borderRadius: variables.borderRadiusLarge, |
||||
|
}, |
||||
|
|
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
...primaryCommon, |
||||
|
'.dark': { |
||||
|
...darkCommon, |
||||
|
}, |
||||
|
'.danger': { |
||||
|
...dangerCommon, |
||||
|
}, |
||||
|
'.warning': { |
||||
|
...warningCommon, |
||||
|
}, |
||||
|
'.info': { |
||||
|
...infoCommon, |
||||
|
}, |
||||
|
'.primary': { |
||||
|
...primaryCommon, |
||||
|
}, |
||||
|
'.success': { |
||||
|
...successCommon, |
||||
|
}, |
||||
|
'.light': { |
||||
|
...lightCommon, |
||||
|
}, |
||||
|
'.disabled': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: variables.buttonDisabledBg, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.buttonDisabledBg, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
'.small': { |
||||
|
height: 30, |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 14, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
fontSize: 20, |
||||
|
paddingTop: 0, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
fontSize: 20, |
||||
|
paddingTop: 0, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
'.large': { |
||||
|
height: 60, |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 22, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
'.capitalize': {}, |
||||
|
|
||||
|
'.vertical': { |
||||
|
flexDirection: 'column', |
||||
|
height: null, |
||||
|
}, |
||||
|
|
||||
|
'NativeBase.Text': { |
||||
|
fontFamily: variables.buttonFontFamily, |
||||
|
marginLeft: 0, |
||||
|
marginRight: 0, |
||||
|
color: variables.inverseTextColor, |
||||
|
fontSize: variables.buttonTextSize, |
||||
|
paddingHorizontal: 16, |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
|
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.inverseTextColor, |
||||
|
fontSize: 24, |
||||
|
marginHorizontal: 16, |
||||
|
paddingTop: platform === PLATFORM.IOS ? 2 : undefined, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.inverseTextColor, |
||||
|
fontSize: 24, |
||||
|
marginHorizontal: 16, |
||||
|
paddingTop: platform === PLATFORM.IOS ? 2 : undefined, |
||||
|
}, |
||||
|
|
||||
|
'.iconLeft': { |
||||
|
'NativeBase.Text': { |
||||
|
marginLeft: 0, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
marginRight: 0, |
||||
|
marginLeft: 16, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginRight: 0, |
||||
|
marginLeft: 16, |
||||
|
}, |
||||
|
}, |
||||
|
'.iconRight': { |
||||
|
'NativeBase.Text': { |
||||
|
marginRight: 0, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
marginLeft: 0, |
||||
|
marginRight: 16, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginLeft: 0, |
||||
|
marginRight: 16, |
||||
|
}, |
||||
|
}, |
||||
|
'.picker': { |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
fontSize: 16, |
||||
|
lineHeight: null, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
paddingVertical: variables.buttonPadding, |
||||
|
backgroundColor: variables.buttonPrimaryBg, |
||||
|
borderRadius: variables.borderRadiusBase, |
||||
|
borderColor: variables.buttonPrimaryBg, |
||||
|
borderWidth: null, |
||||
|
height: 45, |
||||
|
flexDirection: 'row', |
||||
|
elevation: 2, |
||||
|
shadowColor: platformStyle === PLATFORM.MATERIAL ? variables.brandDark : undefined, |
||||
|
shadowOffset: platformStyle === PLATFORM.MATERIAL ? { width: 0, height: 2 } : undefined, |
||||
|
shadowOpacity: platformStyle === PLATFORM.MATERIAL ? 0.2 : undefined, |
||||
|
shadowRadius: platformStyle === PLATFORM.MATERIAL ? 1.2 : undefined, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'space-between', |
||||
|
}; |
||||
|
return buttonTheme; |
||||
|
}; |
||||
@ -0,0 +1,37 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const cardTheme = { |
||||
|
'.transparent': { |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowOpacity: null, |
||||
|
shadowRadius: null, |
||||
|
elevation: null, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderWidth: 0, |
||||
|
}, |
||||
|
'.noShadow': { |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowOpacity: null, |
||||
|
elevation: null, |
||||
|
}, |
||||
|
marginVertical: 5, |
||||
|
marginHorizontal: 2, |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderRadius: variables.cardBorderRadius, |
||||
|
borderColor: variables.cardBorderColor, |
||||
|
flexWrap: 'nowrap', |
||||
|
backgroundColor: variables.cardDefaultBg, |
||||
|
shadowColor: '#000', |
||||
|
shadowOffset: { width: 0, height: 2 }, |
||||
|
shadowOpacity: 0.1, |
||||
|
shadowRadius: 1.5, |
||||
|
elevation: 3, |
||||
|
}; |
||||
|
|
||||
|
return cardTheme; |
||||
|
}; |
||||
@ -0,0 +1,198 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { StyleSheet } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platform } = variables; |
||||
|
const transparentBtnCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: variables.DefaultFontSize - 3, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
marginHorizontal: null, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
}, |
||||
|
paddingVertical: null, |
||||
|
paddingHorizontal: null, |
||||
|
}; |
||||
|
|
||||
|
const cardItemTheme = { |
||||
|
'NativeBase.Left': { |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '400', |
||||
|
marginRight: 20, |
||||
|
}, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
marginLeft: 10, |
||||
|
alignItems: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
fontSize: variables.iconFontSize, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
fontSize: variables.iconFontSize, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
marginLeft: 10, |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
...transparentBtnCommon, |
||||
|
paddingRight: variables.cardItemPadding + 5, |
||||
|
}, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
}, |
||||
|
'.content': { |
||||
|
'NativeBase.Text': { |
||||
|
color: platform === PLATFORM.IOS ? '#555' : '#222', |
||||
|
fontSize: variables.DefaultFontSize - 2, |
||||
|
}, |
||||
|
}, |
||||
|
'.cardBody': { |
||||
|
padding: -5, |
||||
|
'NativeBase.Text': { |
||||
|
marginTop: 5, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
marginRight: 20, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
...transparentBtnCommon, |
||||
|
paddingRight: variables.cardItemPadding + 5, |
||||
|
alignSelf: 'stretch', |
||||
|
}, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
alignSelf: 'stretch', |
||||
|
alignItems: 'flex-start', |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Badge': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
...transparentBtnCommon, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
alignSelf: null, |
||||
|
fontSize: variables.iconFontSize - 8, |
||||
|
color: variables.cardBorderColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
alignSelf: null, |
||||
|
fontSize: variables.iconFontSize - 8, |
||||
|
color: variables.cardBorderColor, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: variables.DefaultFontSize - 1, |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Thumbnail': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Image': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Radio': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Checkbox': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Switch': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
flex: 0.8, |
||||
|
}, |
||||
|
'.header': { |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 16, |
||||
|
fontWeight: platform === PLATFORM.IOS ? '600' : '500', |
||||
|
}, |
||||
|
'.bordered': { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.brandPrimary, |
||||
|
fontWeight: platform === PLATFORM.IOS ? '600' : '500', |
||||
|
}, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
}, |
||||
|
borderBottomWidth: null, |
||||
|
paddingVertical: variables.cardItemPadding + 5, |
||||
|
}, |
||||
|
'.footer': { |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 16, |
||||
|
fontWeight: platform === PLATFORM.IOS ? '600' : '500', |
||||
|
}, |
||||
|
'.bordered': { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.brandPrimary, |
||||
|
fontWeight: platform === PLATFORM.IOS ? '600' : '500', |
||||
|
}, |
||||
|
borderTopWidth: variables.borderWidth, |
||||
|
}, |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
width: variables.iconFontSize + 5, |
||||
|
fontSize: variables.iconFontSize - 2, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
width: variables.iconFontSize + 5, |
||||
|
fontSize: variables.iconFontSize - 2, |
||||
|
}, |
||||
|
'.bordered': { |
||||
|
borderBottomWidth: StyleSheet.hairlineWidth, |
||||
|
borderColor: variables.cardBorderColor, |
||||
|
}, |
||||
|
'.first': { |
||||
|
borderTopLeftRadius: variables.cardBorderRadius, |
||||
|
borderTopRightRadius: variables.cardBorderRadius, |
||||
|
}, |
||||
|
'.last': { |
||||
|
borderBottomLeftRadius: variables.cardBorderRadius, |
||||
|
borderBottomRightRadius: variables.cardBorderRadius, |
||||
|
}, |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
borderRadius: variables.cardBorderRadius, |
||||
|
padding: variables.cardItemPadding + 5, |
||||
|
paddingVertical: variables.cardItemPadding, |
||||
|
backgroundColor: variables.cardDefaultBg, |
||||
|
}; |
||||
|
|
||||
|
return cardItemTheme; |
||||
|
}; |
||||
@ -0,0 +1,38 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const checkBoxTheme = { |
||||
|
'.checked': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.checkboxTickColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.checkboxTickColor, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: 'transparent', |
||||
|
lineHeight: variables.CheckboxIconSize, |
||||
|
marginTop: variables.CheckboxIconMarginTop, |
||||
|
fontSize: variables.CheckboxFontSize, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: 'transparent', |
||||
|
lineHeight: variables.CheckboxIconSize, |
||||
|
marginTop: variables.CheckboxIconMarginTop, |
||||
|
fontSize: variables.CheckboxFontSize, |
||||
|
}, |
||||
|
borderRadius: variables.CheckboxRadius, |
||||
|
overflow: 'hidden', |
||||
|
width: variables.checkboxSize, |
||||
|
height: variables.checkboxSize, |
||||
|
borderWidth: variables.CheckboxBorderWidth, |
||||
|
paddingLeft: variables.CheckboxPaddingLeft - 1, |
||||
|
paddingBottom: variables.CheckboxPaddingBottom, |
||||
|
left: 10, |
||||
|
}; |
||||
|
|
||||
|
return checkBoxTheme; |
||||
|
}; |
||||
@ -0,0 +1,17 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { Platform, Dimensions } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
const deviceHeight = Dimensions.get('window').height; |
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const theme = { |
||||
|
flex: 1, |
||||
|
height: Platform.OS === PLATFORM.IOS ? deviceHeight : deviceHeight - 20, |
||||
|
backgroundColor: variables.containerBgColor, |
||||
|
}; |
||||
|
|
||||
|
return theme; |
||||
|
}; |
||||
@ -0,0 +1,14 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const contentTheme = { |
||||
|
flex: 1, |
||||
|
backgroundColor: 'transparent', |
||||
|
'NativeBase.Segment': { |
||||
|
borderWidth: 0, |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
return contentTheme; |
||||
|
}; |
||||
@ -0,0 +1,25 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const fabTheme = { |
||||
|
'NativeBase.Button': { |
||||
|
alignItems: 'center', |
||||
|
padding: null, |
||||
|
justifyContent: 'center', |
||||
|
'NativeBase.Icon': { |
||||
|
alignSelf: 'center', |
||||
|
fontSize: 20, |
||||
|
marginLeft: 0, |
||||
|
marginRight: 0, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
alignSelf: 'center', |
||||
|
fontSize: 20, |
||||
|
marginLeft: 0, |
||||
|
marginRight: 0, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
return fabTheme; |
||||
|
}; |
||||
@ -0,0 +1,117 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platformStyle } = variables; |
||||
|
const { platform } = variables; |
||||
|
|
||||
|
const iconCommon = { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
}, |
||||
|
}; |
||||
|
const iconNBCommon = { |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
}, |
||||
|
}; |
||||
|
const textCommon = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
}, |
||||
|
}; |
||||
|
const footerTheme = { |
||||
|
'NativeBase.Left': { |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
...textCommon, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
// ...textCommon
|
||||
|
}, |
||||
|
flex: 1, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'flex-start', |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
flex: 1, |
||||
|
alignItems: 'center', |
||||
|
alignSelf: 'center', |
||||
|
flexDirection: 'row', |
||||
|
'NativeBase.Button': { |
||||
|
alignSelf: 'center', |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
...textCommon, |
||||
|
}, |
||||
|
'.full': { |
||||
|
height: variables.footerHeight, |
||||
|
paddingBottom: variables.footerPaddingBottom, |
||||
|
flex: 1, |
||||
|
}, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
// ...textCommon
|
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
...textCommon, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
...iconCommon, |
||||
|
...iconNBCommon, |
||||
|
// ...textCommon
|
||||
|
}, |
||||
|
flex: 1, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'flex-end', |
||||
|
}, |
||||
|
backgroundColor: variables.footerDefaultBg, |
||||
|
flexDirection: 'row', |
||||
|
justifyContent: 'center', |
||||
|
borderTopWidth: |
||||
|
platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL |
||||
|
? variables.borderWidth |
||||
|
: undefined, |
||||
|
borderColor: |
||||
|
platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? '#cbcbcb' : undefined, |
||||
|
height: variables.footerHeight, |
||||
|
paddingBottom: variables.footerPaddingBottom, |
||||
|
elevation: 3, |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
}; |
||||
|
return footerTheme; |
||||
|
}; |
||||
@ -0,0 +1,78 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { Platform } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platform } = variables; |
||||
|
|
||||
|
const footerTabTheme = { |
||||
|
'NativeBase.Button': { |
||||
|
'.active': { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
fontSize: variables.tabBarTextSize, |
||||
|
lineHeight: 16, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.tabBarActiveTextColor, |
||||
|
}, |
||||
|
backgroundColor: variables.tabActiveBgColor, |
||||
|
}, |
||||
|
flexDirection: null, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
alignSelf: 'center', |
||||
|
flex: 1, |
||||
|
height: variables.footerHeight, |
||||
|
justifyContent: 'center', |
||||
|
'.badge': { |
||||
|
'NativeBase.Badge': { |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 11, |
||||
|
fontWeight: platform === PLATFORM.IOS ? '600' : undefined, |
||||
|
lineHeight: 14, |
||||
|
}, |
||||
|
top: -3, |
||||
|
alignSelf: 'center', |
||||
|
left: 10, |
||||
|
zIndex: 99, |
||||
|
height: 18, |
||||
|
padding: 1.7, |
||||
|
paddingHorizontal: 3, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginTop: -18, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.tabBarTextColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.tabBarTextColor, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.tabBarTextColor, |
||||
|
fontSize: variables.tabBarTextSize, |
||||
|
lineHeight: 16, |
||||
|
}, |
||||
|
}, |
||||
|
backgroundColor: Platform.OS === PLATFORM.ANDROID ? variables.footerDefaultBg : undefined, |
||||
|
flexDirection: 'row', |
||||
|
justifyContent: 'space-between', |
||||
|
flex: 1, |
||||
|
alignSelf: 'stretch', |
||||
|
}; |
||||
|
|
||||
|
return footerTabTheme; |
||||
|
}; |
||||
@ -0,0 +1,86 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const theme = { |
||||
|
'NativeBase.Item': { |
||||
|
'.fixedLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
paddingLeft: null, |
||||
|
}, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
'.inlineLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
paddingLeft: null, |
||||
|
}, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
'.placeholderLabel': { |
||||
|
'NativeBase.Input': {}, |
||||
|
}, |
||||
|
'.stackedLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
top: 5, |
||||
|
paddingLeft: null, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: null, |
||||
|
marginLeft: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginTop: 36, |
||||
|
}, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
'.floatingLabel': { |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: null, |
||||
|
top: 10, |
||||
|
marginLeft: null, |
||||
|
}, |
||||
|
'NativeBase.Label': { |
||||
|
left: 0, |
||||
|
top: 6, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
top: 6, |
||||
|
}, |
||||
|
marginTop: 15, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
'NativeBase.Label': { |
||||
|
left: 0, |
||||
|
}, |
||||
|
marginLeft: 0, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
'NativeBase.Label': { |
||||
|
left: 0, |
||||
|
}, |
||||
|
marginLeft: 0, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
'NativeBase.Label': { |
||||
|
left: 0, |
||||
|
top: 0, |
||||
|
position: 'relative', |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
left: -15, |
||||
|
}, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
'.last': { |
||||
|
marginLeft: 0, |
||||
|
paddingLeft: 15, |
||||
|
}, |
||||
|
'NativeBase.Label': { |
||||
|
paddingRight: 5, |
||||
|
}, |
||||
|
marginLeft: 15, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
return theme; |
||||
|
}; |
||||
@ -0,0 +1,13 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const h1Theme = { |
||||
|
color: variables.textColor, |
||||
|
fontSize: variables.fontSizeH1, |
||||
|
lineHeight: variables.lineHeightH1, |
||||
|
}; |
||||
|
|
||||
|
return h1Theme; |
||||
|
}; |
||||
@ -0,0 +1,13 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const h2Theme = { |
||||
|
color: variables.textColor, |
||||
|
fontSize: variables.fontSizeH2, |
||||
|
lineHeight: variables.lineHeightH2, |
||||
|
}; |
||||
|
|
||||
|
return h2Theme; |
||||
|
}; |
||||
@ -0,0 +1,13 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const h3Theme = { |
||||
|
color: variables.textColor, |
||||
|
fontSize: variables.fontSizeH3, |
||||
|
lineHeight: variables.lineHeightH3, |
||||
|
}; |
||||
|
|
||||
|
return h3Theme; |
||||
|
}; |
||||
@ -0,0 +1,389 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { PixelRatio, StatusBar } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platformStyle } = variables; |
||||
|
const { platform } = variables; |
||||
|
|
||||
|
const headerTheme = { |
||||
|
'.span': { |
||||
|
height: 128, |
||||
|
'NativeBase.Left': { |
||||
|
alignSelf: 'flex-start', |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
alignSelf: 'flex-end', |
||||
|
alignItems: 'flex-start', |
||||
|
justifyContent: 'center', |
||||
|
paddingBottom: 26, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
alignSelf: 'flex-start', |
||||
|
}, |
||||
|
}, |
||||
|
'.hasSubtitle': { |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Title': { |
||||
|
fontSize: variables.titleFontSize - 2, |
||||
|
fontFamily: variables.titleFontfamily, |
||||
|
textAlign: 'center', |
||||
|
fontWeight: '500', |
||||
|
paddingBottom: 3, |
||||
|
}, |
||||
|
'NativeBase.Subtitle': { |
||||
|
fontSize: variables.subTitleFontSize, |
||||
|
fontFamily: variables.titleFontfamily, |
||||
|
color: variables.subtitleColor, |
||||
|
textAlign: 'center', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
borderBottomColor: 'transparent', |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
paddingTop: platform === PLATFORM.ANDROID ? StatusBar.currentHeight : undefined, |
||||
|
height: |
||||
|
platform === PLATFORM.ANDROID |
||||
|
? variables.toolbarHeight + StatusBar.currentHeight |
||||
|
: variables.toolbarHeight, |
||||
|
}, |
||||
|
'.noShadow': { |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
}, |
||||
|
'.hasTabs': { |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'.hasSegment': { |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
borderBottomWidth: null, |
||||
|
'NativeBase.Left': { |
||||
|
flex: 0.3, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
flex: 0.3, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
flex: 1, |
||||
|
'NativeBase.Segment': { |
||||
|
marginRight: 0, |
||||
|
alignSelf: 'center', |
||||
|
'NativeBase.Button': { |
||||
|
paddingLeft: 0, |
||||
|
paddingRight: 0, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'.noLeft': { |
||||
|
'NativeBase.Left': { |
||||
|
width: platform === PLATFORM.IOS ? undefined : 0, |
||||
|
flex: platform === PLATFORM.IOS ? 1 : 0, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Title': { |
||||
|
paddingLeft: platform === PLATFORM.IOS ? undefined : 10, |
||||
|
}, |
||||
|
'NativeBase.Subtitle': { |
||||
|
paddingLeft: platform === PLATFORM.IOS ? undefined : 10, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
justifyContent: 'center', |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'center', |
||||
|
'.transparent': { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.toolbarBtnTextColor, |
||||
|
fontWeight: '600', |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
paddingHorizontal: variables.buttonPadding, |
||||
|
}, |
||||
|
paddingHorizontal: 15, |
||||
|
}, |
||||
|
'.searchBar': { |
||||
|
'NativeBase.Item': { |
||||
|
'NativeBase.Icon': { |
||||
|
backgroundColor: 'transparent', |
||||
|
color: variables.dropdownLinkColor, |
||||
|
fontSize: variables.toolbarSearchIconSize, |
||||
|
alignItems: 'center', |
||||
|
marginTop: 2, |
||||
|
paddingRight: 10, |
||||
|
paddingLeft: 10, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
backgroundColor: 'transparent', |
||||
|
color: null, |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
alignSelf: 'center', |
||||
|
lineHeight: null, |
||||
|
height: variables.searchBarInputHeight, |
||||
|
}, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'flex-start', |
||||
|
flex: 1, |
||||
|
height: variables.searchBarHeight, |
||||
|
borderColor: 'transparent', |
||||
|
backgroundColor: variables.toolbarInputColor, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
'NativeBase.Text': { |
||||
|
fontWeight: '500', |
||||
|
}, |
||||
|
paddingHorizontal: null, |
||||
|
paddingLeft: platform === PLATFORM.IOS ? 10 : null, |
||||
|
}, |
||||
|
paddingHorizontal: platform === PLATFORM.IOS ? undefined : null, |
||||
|
width: platform === PLATFORM.IOS ? undefined : 0, |
||||
|
height: platform === PLATFORM.IOS ? undefined : 0, |
||||
|
}, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
'NativeBase.Item': { |
||||
|
borderRadius: platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? 25 : 3, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Left': { |
||||
|
'NativeBase.Button': { |
||||
|
'.hasText': { |
||||
|
marginLeft: -10, |
||||
|
height: 30, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize, |
||||
|
marginTop: 2, |
||||
|
marginRight: 5, |
||||
|
marginLeft: 2, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.toolbarBtnTextColor, |
||||
|
fontSize: platform === PLATFORM.IOS ? 17 : 0, |
||||
|
marginLeft: 7, |
||||
|
lineHeight: 19.5, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize, |
||||
|
marginTop: 2, |
||||
|
marginRight: 5, |
||||
|
marginLeft: 2, |
||||
|
}, |
||||
|
}, |
||||
|
'.transparent': { |
||||
|
marginLeft: platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? -3 : 0, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: |
||||
|
platform === PLATFORM.IOS && variables.platformStyle !== PLATFORM.MATERIAL |
||||
|
? variables.iconHeaderSize + 1 |
||||
|
: variables.iconHeaderSize, |
||||
|
marginTop: 0, |
||||
|
marginRight: 2, |
||||
|
marginLeft: 1, |
||||
|
paddingTop: 1, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: |
||||
|
platform === PLATFORM.IOS && variables.platformStyle !== PLATFORM.MATERIAL |
||||
|
? variables.iconHeaderSize + 1 |
||||
|
: variables.iconHeaderSize - 2, |
||||
|
marginTop: 0, |
||||
|
marginRight: 2, |
||||
|
marginLeft: 1, |
||||
|
paddingTop: 1, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.toolbarBtnTextColor, |
||||
|
fontSize: platform === PLATFORM.IOS ? 17 : 0, |
||||
|
top: platform === PLATFORM.IOS ? 1 : -1.5, |
||||
|
paddingLeft: platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? 2 : 5, |
||||
|
paddingRight: |
||||
|
platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? undefined : 10, |
||||
|
}, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
paddingRight: variables.buttonPadding, |
||||
|
paddingLeft: platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? 4 : 8, |
||||
|
}, |
||||
|
flex: platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? 1 : 0.4, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'flex-start', |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
flex: 1, |
||||
|
alignItems: |
||||
|
platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL ? 'center' : 'flex-start', |
||||
|
alignSelf: 'center', |
||||
|
'NativeBase.Segment': { |
||||
|
borderWidth: 0, |
||||
|
alignSelf: 'flex-end', |
||||
|
marginRight: platform === PLATFORM.IOS ? -40 : -55, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
alignSelf: 'center', |
||||
|
'.transparent': { |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.inverseTextColor, |
||||
|
backgroundColor: 'transparent', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Button': { |
||||
|
'.hasText': { |
||||
|
height: 30, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize - 2, |
||||
|
marginTop: 2, |
||||
|
marginRight: 2, |
||||
|
marginLeft: 5, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.toolbarBtnTextColor, |
||||
|
fontSize: platform === PLATFORM.IOS ? 17 : 14, |
||||
|
lineHeight: 19.5, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize - 2, |
||||
|
marginTop: 2, |
||||
|
marginRight: 2, |
||||
|
marginLeft: 5, |
||||
|
}, |
||||
|
}, |
||||
|
'.transparent': { |
||||
|
marginRight: platform === PLATFORM.IOS ? -9 : -5, |
||||
|
paddingLeft: 15, |
||||
|
paddingRight: 12, |
||||
|
paddingHorizontal: 15, |
||||
|
borderRadius: 50, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize - 2, |
||||
|
marginTop: 0, |
||||
|
marginLeft: 2, |
||||
|
marginRight: 0, |
||||
|
// paddingTop: 0
|
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
fontSize: variables.iconHeaderSize - 2, |
||||
|
marginTop: 0, |
||||
|
marginLeft: 2, |
||||
|
marginRight: 0, |
||||
|
// paddingTop: 0
|
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.toolbarBtnTextColor, |
||||
|
fontSize: platform === PLATFORM.IOS ? 17 : 14, |
||||
|
top: platform === PLATFORM.IOS ? 1 : -1.5, |
||||
|
paddingRight: |
||||
|
platform === PLATFORM.IOS && variables.platformStyle !== PLATFORM.MATERIAL |
||||
|
? 0 |
||||
|
: undefined, |
||||
|
}, |
||||
|
backgroundColor: 'transparent', |
||||
|
borderColor: null, |
||||
|
elevation: 0, |
||||
|
shadowColor: null, |
||||
|
shadowOffset: null, |
||||
|
shadowRadius: null, |
||||
|
shadowOpacity: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.toolbarBtnColor, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
paddingHorizontal: variables.buttonPadding, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'flex-end', |
||||
|
flexDirection: 'row', |
||||
|
justifyContent: 'flex-end', |
||||
|
}, |
||||
|
backgroundColor: variables.toolbarDefaultBg, |
||||
|
flexDirection: 'row', |
||||
|
// paddingHorizontal: 10,
|
||||
|
paddingLeft: |
||||
|
platform === PLATFORM.IOS && variables.platformStyle !== PLATFORM.MATERIAL ? 6 : 10, |
||||
|
paddingRight: 10, |
||||
|
justifyContent: 'center', |
||||
|
paddingTop: platform === PLATFORM.IOS ? 18 : 0, |
||||
|
borderBottomWidth: platform === PLATFORM.IOS ? 1 / PixelRatio.getPixelSizeForLayoutSize(1) : 0, |
||||
|
borderBottomColor: variables.toolbarDefaultBorder, |
||||
|
height: |
||||
|
variables.platform === PLATFORM.IOS && variables.platformStyle === PLATFORM.MATERIAL |
||||
|
? variables.toolbarHeight + 10 |
||||
|
: variables.toolbarHeight, |
||||
|
elevation: 3, |
||||
|
shadowColor: platformStyle === PLATFORM.MATERIAL ? '#000' : undefined, |
||||
|
shadowOffset: platformStyle === PLATFORM.MATERIAL ? { width: 0, height: 2 } : undefined, |
||||
|
shadowOpacity: platformStyle === PLATFORM.MATERIAL ? 0.2 : undefined, |
||||
|
shadowRadius: platformStyle === PLATFORM.MATERIAL ? 1.2 : undefined, |
||||
|
top: 0, |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
}; |
||||
|
|
||||
|
return headerTheme; |
||||
|
}; |
||||
@ -0,0 +1,12 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const iconTheme = { |
||||
|
fontSize: variables.iconFontSize, |
||||
|
color: variable.textColor, |
||||
|
}; |
||||
|
|
||||
|
return iconTheme; |
||||
|
}; |
||||
@ -0,0 +1,19 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const inputTheme = { |
||||
|
'.multiline': { |
||||
|
height: null, |
||||
|
}, |
||||
|
height: variables.inputHeightBase, |
||||
|
color: variables.inputColor, |
||||
|
paddingLeft: 5, |
||||
|
paddingRight: 5, |
||||
|
flex: 1, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}; |
||||
|
|
||||
|
return inputTheme; |
||||
|
}; |
||||
@ -0,0 +1,132 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const inputGroupTheme = { |
||||
|
'NativeBase.Icon': { |
||||
|
fontSize: 24, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
paddingHorizontal: 5, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
fontSize: 24, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
paddingHorizontal: 5, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
height: variables.inputHeightBase, |
||||
|
color: variables.inputColor, |
||||
|
paddingLeft: 5, |
||||
|
paddingRight: 5, |
||||
|
flex: 1, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
lineHeight: variables.inputLineHeight, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
paddingLeft: 5, |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
paddingLeft: 5, |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
paddingLeft: 5, |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderRadius: variables.inputGroupRoundedBorderRadius, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
|
||||
|
'.success': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
borderRadius: 30, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
|
||||
|
'.error': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
borderRadius: 30, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.disabled': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: '#384850', |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: '#384850', |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
paddingLeft: 5, |
||||
|
borderWidth: variables.borderWidth, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
backgroundColor: 'transparent', |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
}; |
||||
|
|
||||
|
return inputGroupTheme; |
||||
|
}; |
||||
@ -0,0 +1,241 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { Platform } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const itemTheme = { |
||||
|
'.floatingLabel': { |
||||
|
'NativeBase.Input': { |
||||
|
height: 50, |
||||
|
top: 8, |
||||
|
paddingTop: 3, |
||||
|
paddingBottom: 7, |
||||
|
'.multiline': { |
||||
|
minHeight: variables.inputHeightBase, |
||||
|
paddingTop: Platform.OS === PLATFORM.IOS ? 10 : 3, |
||||
|
paddingBottom: Platform.OS === PLATFORM.IOS ? 14 : 10, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Label': { |
||||
|
paddingTop: 5, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
top: 6, |
||||
|
paddingTop: 8, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
top: 6, |
||||
|
paddingTop: 8, |
||||
|
}, |
||||
|
}, |
||||
|
'.fixedLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
position: null, |
||||
|
top: null, |
||||
|
left: null, |
||||
|
right: null, |
||||
|
flex: 1, |
||||
|
height: null, |
||||
|
width: null, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
flex: 2, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
}, |
||||
|
'.stackedLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
position: null, |
||||
|
top: null, |
||||
|
left: null, |
||||
|
right: null, |
||||
|
paddingTop: 5, |
||||
|
alignSelf: 'flex-start', |
||||
|
fontSize: variables.inputFontSize - 2, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginTop: 36, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
alignSelf: Platform.OS === PLATFORM.IOS ? 'stretch' : 'flex-start', |
||||
|
flex: 1, |
||||
|
width: Platform.OS === PLATFORM.IOS ? null : variables.deviceWidth - 25, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
lineHeight: variables.inputLineHeight - 6, |
||||
|
'.secureTextEntry': { |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
'.multiline': { |
||||
|
paddingTop: Platform.OS === PLATFORM.IOS ? 9 : undefined, |
||||
|
paddingBottom: Platform.OS === PLATFORM.IOS ? 9 : undefined, |
||||
|
}, |
||||
|
}, |
||||
|
flexDirection: null, |
||||
|
minHeight: variables.inputHeightBase + 15, |
||||
|
}, |
||||
|
'.inlineLabel': { |
||||
|
'NativeBase.Label': { |
||||
|
position: null, |
||||
|
top: null, |
||||
|
left: null, |
||||
|
right: null, |
||||
|
paddingRight: 20, |
||||
|
height: null, |
||||
|
width: null, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: 5, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
flexDirection: 'row', |
||||
|
}, |
||||
|
'NativeBase.Label': { |
||||
|
fontSize: variables.inputFontSize, |
||||
|
color: variables.inputColorPlaceholder, |
||||
|
paddingRight: 5, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
fontSize: 24, |
||||
|
paddingRight: 8, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
fontSize: 24, |
||||
|
paddingRight: 8, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
'.multiline': { |
||||
|
height: null, |
||||
|
}, |
||||
|
height: variables.inputHeightBase, |
||||
|
color: variables.inputColor, |
||||
|
flex: 1, |
||||
|
top: Platform.OS === PLATFORM.IOS ? 1.5 : undefined, |
||||
|
fontSize: variables.inputFontSize, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: 15, |
||||
|
}, |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: 8, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
paddingLeft: 10, |
||||
|
}, |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
'NativeBase.Input': { |
||||
|
paddingLeft: 8, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
paddingLeft: 10, |
||||
|
}, |
||||
|
'.success': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.error': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderRadius: 30, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
}, |
||||
|
|
||||
|
'.success': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
borderRadius: 30, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
borderColor: variables.inputSuccessBorderColor, |
||||
|
}, |
||||
|
|
||||
|
'.error': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.rounded': { |
||||
|
borderRadius: 30, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.regular': { |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.underline': { |
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
borderColor: variables.inputErrorBorderColor, |
||||
|
}, |
||||
|
'.disabled': { |
||||
|
'NativeBase.Icon': { |
||||
|
color: '#384850', |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: '#384850', |
||||
|
}, |
||||
|
}, |
||||
|
'.picker': { |
||||
|
marginLeft: 0, |
||||
|
}, |
||||
|
|
||||
|
borderWidth: variables.borderWidth * 2, |
||||
|
borderTopWidth: 0, |
||||
|
borderRightWidth: 0, |
||||
|
borderLeftWidth: 0, |
||||
|
borderColor: variables.inputBorderColor, |
||||
|
backgroundColor: 'transparent', |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
marginLeft: 2, |
||||
|
}; |
||||
|
|
||||
|
return itemTheme; |
||||
|
}; |
||||
@ -0,0 +1,15 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const labelTheme = { |
||||
|
'.focused': { |
||||
|
width: 0, |
||||
|
}, |
||||
|
'.textPrimary': { |
||||
|
color: 'gray', |
||||
|
}, |
||||
|
fontSize: 17, |
||||
|
}; |
||||
|
|
||||
|
return labelTheme; |
||||
|
}; |
||||
@ -0,0 +1,11 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const leftTheme = { |
||||
|
flex: 1, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'flex-start', |
||||
|
}; |
||||
|
|
||||
|
return leftTheme; |
||||
|
}; |
||||
@ -0,0 +1,441 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { Platform, PixelRatio } from 'react-native'; |
||||
|
|
||||
|
import pickerTheme from './Picker'; |
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const { platform } = variables; |
||||
|
const selectedStyle = { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.listItemSelected, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: variables.listItemSelected, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
const listItemTheme = { |
||||
|
'NativeBase.InputGroup': { |
||||
|
'NativeBase.Icon': { |
||||
|
paddingRight: 5, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
paddingRight: 5, |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
paddingHorizontal: 5, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
borderWidth: null, |
||||
|
margin: -10, |
||||
|
borderBottomColor: 'transparent', |
||||
|
}, |
||||
|
'.searchBar': { |
||||
|
'NativeBase.Item': { |
||||
|
'NativeBase.Icon': { |
||||
|
backgroundColor: 'transparent', |
||||
|
color: variables.dropdownLinkColor, |
||||
|
fontSize: |
||||
|
platform === PLATFORM.IOS ? variables.iconFontSize - 10 : variables.iconFontSize - 5, |
||||
|
alignItems: 'center', |
||||
|
marginTop: 2, |
||||
|
paddingRight: 8, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
backgroundColor: 'transparent', |
||||
|
color: null, |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
'NativeBase.Input': { |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
alignSelf: 'center', |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'flex-start', |
||||
|
flex: 1, |
||||
|
height: platform === PLATFORM.IOS ? 30 : 40, |
||||
|
borderColor: 'transparent', |
||||
|
backgroundColor: '#fff', |
||||
|
borderRadius: 5, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
'NativeBase.Text': { |
||||
|
fontWeight: '500', |
||||
|
}, |
||||
|
paddingHorizontal: null, |
||||
|
paddingLeft: platform === PLATFORM.IOS ? 10 : null, |
||||
|
}, |
||||
|
paddingHorizontal: platform === PLATFORM.IOS ? undefined : null, |
||||
|
width: platform === PLATFORM.IOS ? undefined : 0, |
||||
|
height: platform === PLATFORM.IOS ? undefined : 0, |
||||
|
}, |
||||
|
backgroundColor: variables.toolbarInputColor, |
||||
|
padding: 10, |
||||
|
marginLeft: null, |
||||
|
}, |
||||
|
'NativeBase.CheckBox': { |
||||
|
marginLeft: -10, |
||||
|
marginRight: 10, |
||||
|
}, |
||||
|
'.first': { |
||||
|
'.itemHeader': { |
||||
|
paddingTop: variables.listItemPadding + 3, |
||||
|
}, |
||||
|
}, |
||||
|
'.itemHeader': { |
||||
|
'.first': { |
||||
|
paddingTop: variables.listItemPadding + 3, |
||||
|
}, |
||||
|
borderBottomWidth: platform === PLATFORM.IOS ? variables.borderWidth : null, |
||||
|
marginLeft: null, |
||||
|
padding: variables.listItemPadding, |
||||
|
paddingLeft: variables.listItemPadding + 5, |
||||
|
paddingTop: platform === PLATFORM.IOS ? variables.listItemPadding + 25 : undefined, |
||||
|
paddingBottom: platform === PLATFORM.ANDROID ? variables.listItemPadding + 20 : undefined, |
||||
|
flexDirection: 'row', |
||||
|
borderColor: variables.listBorderColor, |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: 14, |
||||
|
color: platform === PLATFORM.IOS ? undefined : variables.listNoteColor, |
||||
|
}, |
||||
|
}, |
||||
|
'.itemDivider': { |
||||
|
borderBottomWidth: null, |
||||
|
marginLeft: null, |
||||
|
padding: variables.listItemPadding, |
||||
|
paddingLeft: variables.listItemPadding + 5, |
||||
|
backgroundColor: variables.listDividerBg, |
||||
|
flexDirection: 'row', |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}, |
||||
|
'.selected': { |
||||
|
'NativeBase.Left': { |
||||
|
...selectedStyle, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
...selectedStyle, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
...selectedStyle, |
||||
|
}, |
||||
|
...selectedStyle, |
||||
|
}, |
||||
|
'NativeBase.Left': { |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
}, |
||||
|
fontWeight: '600', |
||||
|
}, |
||||
|
marginLeft: 10, |
||||
|
alignItems: null, |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
width: variables.iconFontSize - 10, |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
width: variables.iconFontSize - 10, |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
flexDirection: 'row', |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
marginHorizontal: variables.listItemPadding, |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
}, |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
alignItems: null, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Badge': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.PickerNB': { |
||||
|
'NativeBase.Button': { |
||||
|
marginRight: -15, |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.topTabBarActiveTextColor, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Button': { |
||||
|
alignSelf: null, |
||||
|
'.transparent': { |
||||
|
'NativeBase.Text': { |
||||
|
color: variables.topTabBarActiveTextColor, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
alignSelf: null, |
||||
|
fontSize: variables.iconFontSize - 8, |
||||
|
color: '#c9c8cd', |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
alignSelf: null, |
||||
|
fontSize: variables.iconFontSize - 8, |
||||
|
color: '#c9c8cd', |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
}, |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Thumbnail': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Image': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Radio': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Checkbox': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.Switch': { |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
padding: null, |
||||
|
flex: 0.28, |
||||
|
}, |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
color: variables.listNoteColor, |
||||
|
fontWeight: '200', |
||||
|
}, |
||||
|
alignSelf: 'center', |
||||
|
}, |
||||
|
'.last': { |
||||
|
marginLeft: -(variables.listItemPadding + 5), |
||||
|
paddingLeft: (variables.listItemPadding + 5) * 2, |
||||
|
top: 1, |
||||
|
}, |
||||
|
'.avatar': { |
||||
|
'NativeBase.Left': { |
||||
|
flex: 0, |
||||
|
alignSelf: 'flex-start', |
||||
|
paddingTop: 14, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
marginLeft: null, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
paddingVertical: variables.listItemPadding, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
marginLeft: variables.listItemPadding + 5, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Text': { |
||||
|
'.note': { |
||||
|
fontSize: variables.noteFontSize - 2, |
||||
|
}, |
||||
|
}, |
||||
|
flex: 0, |
||||
|
paddingRight: variables.listItemPadding + 5, |
||||
|
alignSelf: 'stretch', |
||||
|
paddingVertical: variables.listItemPadding, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}, |
||||
|
'.noBorder': { |
||||
|
'NativeBase.Body': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
}, |
||||
|
borderBottomWidth: null, |
||||
|
paddingVertical: null, |
||||
|
paddingRight: null, |
||||
|
}, |
||||
|
'.thumbnail': { |
||||
|
'NativeBase.Left': { |
||||
|
flex: 0, |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
marginLeft: null, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
paddingVertical: variables.listItemPadding + 8, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
marginLeft: variables.listItemPadding + 5, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Button': { |
||||
|
'.transparent': { |
||||
|
'NativeBase.Text': { |
||||
|
fontSize: variables.listNoteSize, |
||||
|
color: variables.sTabBarActiveTextColor, |
||||
|
}, |
||||
|
}, |
||||
|
height: null, |
||||
|
}, |
||||
|
flex: 0, |
||||
|
justifyContent: 'center', |
||||
|
alignSelf: 'stretch', |
||||
|
paddingRight: variables.listItemPadding + 5, |
||||
|
paddingVertical: variables.listItemPadding + 5, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}, |
||||
|
'.noBorder': { |
||||
|
'NativeBase.Body': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
}, |
||||
|
borderBottomWidth: null, |
||||
|
paddingVertical: null, |
||||
|
paddingRight: null, |
||||
|
}, |
||||
|
'.icon': { |
||||
|
'.last': { |
||||
|
'NativeBase.Body': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
borderBottomWidth: variables.borderWidth, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}, |
||||
|
'NativeBase.Left': { |
||||
|
'NativeBase.Button': { |
||||
|
'NativeBase.IconNB': { |
||||
|
marginHorizontal: null, |
||||
|
fontSize: variables.iconFontSize - 5, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
marginHorizontal: null, |
||||
|
fontSize: variables.iconFontSize - 8, |
||||
|
}, |
||||
|
alignSelf: 'center', |
||||
|
height: 29, |
||||
|
width: 29, |
||||
|
borderRadius: 6, |
||||
|
paddingVertical: null, |
||||
|
paddingHorizontal: null, |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
width: variables.iconFontSize - 5, |
||||
|
fontSize: variables.iconFontSize - 2, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
width: variables.iconFontSize - 5, |
||||
|
fontSize: variables.iconFontSize - 2, |
||||
|
}, |
||||
|
paddingRight: variables.listItemPadding + 5, |
||||
|
flex: 0, |
||||
|
height: 44, |
||||
|
justifyContent: 'center', |
||||
|
alignItems: 'center', |
||||
|
}, |
||||
|
'NativeBase.Body': { |
||||
|
'NativeBase.Text': { |
||||
|
marginLeft: null, |
||||
|
fontSize: 17, |
||||
|
}, |
||||
|
flex: 1, |
||||
|
height: 44, |
||||
|
justifyContent: 'center', |
||||
|
borderBottomWidth: 1 / PixelRatio.getPixelSizeForLayoutSize(1), |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
'NativeBase.Text': { |
||||
|
textAlign: 'center', |
||||
|
color: '#8F8E95', |
||||
|
fontSize: 17, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: '#C8C7CC', |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
alignSelf: 'center', |
||||
|
paddingLeft: 10, |
||||
|
paddingTop: 3, |
||||
|
}, |
||||
|
'NativeBase.Icon': { |
||||
|
color: '#C8C7CC', |
||||
|
fontSize: variables.iconFontSize - 10, |
||||
|
alignSelf: 'center', |
||||
|
paddingLeft: 10, |
||||
|
paddingTop: 3, |
||||
|
}, |
||||
|
'NativeBase.Switch': { |
||||
|
marginRight: Platform.OS === PLATFORM.IOS ? undefined : -5, |
||||
|
alignSelf: null, |
||||
|
}, |
||||
|
'NativeBase.PickerNB': { |
||||
|
...pickerTheme(), |
||||
|
}, |
||||
|
flexDirection: 'row', |
||||
|
alignItems: 'center', |
||||
|
flex: 0, |
||||
|
alignSelf: 'stretch', |
||||
|
height: 44, |
||||
|
justifyContent: 'flex-end', |
||||
|
borderBottomWidth: 1 / PixelRatio.getPixelSizeForLayoutSize(1), |
||||
|
borderColor: variables.listBorderColor, |
||||
|
paddingRight: variables.listItemPadding + 5, |
||||
|
}, |
||||
|
'.noBorder': { |
||||
|
'NativeBase.Body': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'NativeBase.Right': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
}, |
||||
|
borderBottomWidth: null, |
||||
|
paddingVertical: null, |
||||
|
paddingRight: null, |
||||
|
height: 44, |
||||
|
justifyContent: 'center', |
||||
|
}, |
||||
|
'.noBorder': { |
||||
|
borderBottomWidth: null, |
||||
|
}, |
||||
|
'.noIndent': { |
||||
|
marginLeft: null, |
||||
|
padding: variables.listItemPadding, |
||||
|
paddingLeft: variables.listItemPadding + 6, |
||||
|
}, |
||||
|
alignItems: 'center', |
||||
|
flexDirection: 'row', |
||||
|
paddingRight: variables.listItemPadding + 6, |
||||
|
paddingVertical: variables.listItemPadding + 3, |
||||
|
marginLeft: variables.listItemPadding + 6, |
||||
|
borderBottomWidth: 1 / PixelRatio.getPixelSizeForLayoutSize(1), |
||||
|
backgroundColor: variables.listBg, |
||||
|
borderColor: variables.listBorderColor, |
||||
|
}; |
||||
|
|
||||
|
return listItemTheme; |
||||
|
}; |
||||
@ -0,0 +1,14 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const pickerTheme = { |
||||
|
'.note': { |
||||
|
color: '#8F8E95', |
||||
|
}, |
||||
|
// width: 90,
|
||||
|
marginRight: -4, |
||||
|
flexGrow: 1, |
||||
|
}; |
||||
|
|
||||
|
return pickerTheme; |
||||
|
}; |
||||
@ -0,0 +1,7 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const pickerTheme = {}; |
||||
|
|
||||
|
return pickerTheme; |
||||
|
}; |
||||
@ -0,0 +1,14 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
export default () => { |
||||
|
const pickerTheme = { |
||||
|
'.note': { |
||||
|
color: '#8F8E95', |
||||
|
}, |
||||
|
// width: 90,
|
||||
|
marginRight: -4, |
||||
|
flexGrow: 1, |
||||
|
}; |
||||
|
|
||||
|
return pickerTheme; |
||||
|
}; |
||||
@ -0,0 +1,26 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import { Platform } from 'react-native'; |
||||
|
|
||||
|
import variable from '../variables/Platform'; |
||||
|
import { PLATFORM } from '../variables/CommonColor'; |
||||
|
|
||||
|
export default (variables /* : * */ = variable) => { |
||||
|
const radioTheme = { |
||||
|
'.selected': { |
||||
|
'NativeBase.IconNB': { |
||||
|
color: |
||||
|
Platform.OS === PLATFORM.IOS ? variables.radioColor : variables.radioSelectedColorAndroid, |
||||
|
lineHeight: Platform.OS === PLATFORM.IOS ? 25 : variables.radioBtnLineHeight, |
||||
|
height: Platform.OS === PLATFORM.IOS ? 20 : undefined, |
||||
|
}, |
||||
|
}, |
||||
|
'NativeBase.IconNB': { |
||||
|
color: Platform.OS === PLATFORM.IOS ? 'transparent' : undefined, |
||||
|
lineHeight: Platform.OS === PLATFORM.IOS ? undefined : variables.radioBtnLineHeight, |
||||
|
fontSize: Platform.OS === PLATFORM.IOS ? undefined : variables.radioBtnSize, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
return radioTheme; |
||||
|
}; |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue