Here's my AppState class:
export default class AppState {
constructor() {
this.navigationState = {
index: 0,
routes: [{ key: "InitialView" }]
};
}
updateNavigationState(type, route = null) {
switch (type) {
case NavigationStateUpdateType.Push:
console.log(this.navigationState.routes.length);
console.log(this.navigationState.routes[0].key);
if (route !== null) {
console.log("Route: " + route);
this.navigationState = NavigationStateUtils.push(this.navigationState, { key: route });
}
case NavigationStateUpdateType.Pop:
this.navigationState = NavigationStateUtils.pop(this.navigationState);
}
}
}
Now if I do this inside InitialView:
this.props.appState.updateNavigationState(NavigationStateUpdateType.Push, "InitialView1");
Nothing happens. It seems like NavigationStateUtil.pushdoesn't work. Here's what the Console looks like:
AppState.js:18 1
AppState.js:19 InitialView
AppState.js:21 Route: InitialView1
AppState.js:18 1
AppState.js:19 InitialView
AppState.js:21 Route: InitialView1
AppState.js:18 1
AppState.js:19 InitialView
AppState.js:21 Route: InitialView1
Why isn't routes being updated? Or am I doing something wrong?
EDIT
My GlobalNavigation component (as asked):
const { CardStack: NavigationCardStack } = NavigationExperimental;
let GlobalNavigation = class GlobalNavigation extends Component {
render() {
return (React.createElement(NavigationCardStack, {renderScene: this._renderScene.bind(this), navigationState: this.props.appState.navigationState}));
}
_renderScene() {
return (React.createElement(InitialView, {appState: this.props.appState}));
}
};
By the way: all this code is generated by TypeSript.
Related
I have a custom plugin in ckeditor5.
When user click on toolbar icon my plugin convert selected test to custom element with a custom attribute name comment-id.
this work properly.
Now I want to watch on click element and get comment-id on click and I don't know how can I do that.
this is the code of my custom plugin
import uploadIcon from './message.svg'
import ButtonView from '#ckeditor/ckeditor5-ui/src/button/buttonview'
import Plugin from '#ckeditor/ckeditor5-core/src/plugin'
import Command from '#ckeditor/ckeditor5-core/src/command'
import './comment.css'
export default class CustomFileExporerPlugin extends Plugin {
init() {
const editor = this.editor
const config = editor.config.get('comment')
console.log(editor.editing.view.document.isFocused)
editor.editing.view.document.on('click', a => {
console.log(a)
})
editor.model.schema.extend('$text', { allowAttributes: 'comment' })
editor.conversion.attributeToElement({
model: 'comment',
view: (commentId, writer) => {
debugger
if (writer) {
return writer.writer.createAttributeElement(
'comment',
{
'comment-id': commentId,
class: `ck-comment-marker`
},
{ priority: 5 }
)
}
}
})
editor.commands.add('comment', new CommentCommand(editor))
editor.ui.componentFactory.add('comment', locale => {
const view = new ButtonView(locale)
view.set({
label: 'add comment',
icon: uploadIcon,
tooltip: true
})
view.on('execute', async () => {
editor.editing.view.focus()
let id = await config.callback()
editor.execute('comment', { value: 'comment', id })
})
return view
})
}
}
class CommentCommand extends Command {
refresh() {
const model = this.editor.model
const doc = model.document
this.value = doc.selection.getAttribute('comment')
this.isEnabled = model.schema.checkAttributeInSelection(
doc.selection,
'comment'
)
}
execute(options = {}) {
const model = this.editor.model
const document = model.document
const selection = document.selection
const highlighter = options.value
model.change(writer => {
const ranges = model.schema.getValidRanges(
selection.getRanges(),
'comment'
)
if (selection.isCollapsed) {
const position = selection.getFirstPosition()
if (selection.hasAttribute('comment')) {
const isSameHighlight = value => {
return (
value.item.hasAttribute('comment') &&
value.item.getAttribute('comment') === this.value
)
}
const highlightStart = position.getLastMatchingPosition(
isSameHighlight,
{ direction: 'backward' }
)
const highlightEnd = position.getLastMatchingPosition(isSameHighlight)
const highlightRange = writer.createRange(
highlightStart,
highlightEnd
)
writer.removeAttribute('comment', highlightRange)
writer.removeSelectionAttribute('comment')
} else if (highlighter) {
writer.setSelectionAttribute('comment', highlighter)
}
} else {
for (const range of ranges) {
writer.setAttribute('comment', options, range)
}
}
})
}
}
When rendering editor, you can catch custom plugin click event using below code.
const command = editor.commands.get('comment')
command.on('execute', () => { catch click event in custom plugin})
I tried my best to write a custom directive in apollo server express to validate if an input type field of type [Int] does not have more than max length but do not know if its the right way to do. Appreciate if somebody could help me correct any mistakes in the code below.
// schema.js
directive #listLength(max: Int) on INPUT_FIELD_DEFINITION
input FiltersInput {
filters: Filters
}
input Filters {
keys: [Int] #listLength(max: 10000)
}
// Custom directive
const { SchemaDirectiveVisitor } = require('apollo-server-express');
import {
GraphQLList,
GraphQLScalarType,
GraphQLInt,
Kind,
DirectiveLocation,
GraphQLDirective
} from "graphql";
export class ListLengthDirective extends SchemaDirectiveVisitor {
static getDirectiveDeclaration(directiveName) {
return new GraphQLDirective({
name: directiveName,
locations: [DirectiveLocation.INPUT_FIELD_DEFINITION],
args: {
max: { type: GraphQLInt },
}
});
}
// Replace field.type with a custom GraphQLScalarType that enforces the
// length restriction.
wrapType(field) {
const fieldName = field.astNode.name.value;
const { type } = field;
if (field.type instanceof GraphQLList) {
field.type = new LimitedLengthType(fieldName, type, this.args.max);
} else {
throw new Error(`Not a scalar type: ${field.type}`);
}
}
visitInputFieldDefinition(field) {
this.wrapType(field);
}
}
class LimitedLengthType extends GraphQLScalarType {
constructor(name, type, maxLength) {
super({
name,
serialize(value) {
return type.serialize(value);
},
parseValue(value) {
value = type.serialize(value);
return type.parseValue(value);
},
parseLiteral(ast) {
switch (ast.kind) {
case Kind.LIST:
if (ast.values.length > maxLength) {
throw {
code: 400,
message: `'${name}' parameter cannot extend ${maxLength} values`,
};
}
const arrayOfInts = ast.values.map(valueObj => parseInt(valueObj['value']));
return arrayOfInts;
}
throw new Error('ast kind should be Int of ListValue')
},
});
}
}
Does this look right?
Thanks
I have a file to save the random id to AsyncStorage.
AppService.js
import Rx from 'rxjs/Rx';
import uuid from 'uuid/v4'
import StorageService from '../storage/StorageService'
export default class AppService {
constructor() {
this.deviceuuid = null
}
rxInit() {
return StorageService.shared.get('deviceuuid').flatMap((deviceuuid) => {
if (deviceuuid == null) {
this.deviceuuid = uuid()
console.log('deviceuuid is empty, create one')
return StorageService.shared.set('deviceuuid', this.deviceuuid)
} else {
this.deviceuuid = deviceuuid
return Rx.Observable.of(this.deviceuuid)
}
}).do((deviceuuid) => {
console.log(`deviceuuid is ${deviceuuid}`)
})
}
}
AppService.shared = new AppService()
StorageService.js
import Rx from 'rxjs/Rx'
import { AsyncStorage } from 'react-native'
import Storage from 'react-native-storage'
let storage = new Storage({
size: 1000,
storageBackend: AsyncStorage,
defaultExpires: null,
})
export default class StorageService {
set(key, value) {
return Rx.Observable.fromPromise(storage.save({key: key, data: value})).map(() => value)
}
get(key) {
return Rx.Observable.fromPromise(storage.load({key: key})).catch(() => Rx.Observable.of(null))
}
remove(key) {
return Rx.Observable.fromPromise(storage.remove({key: key})).catch(() => Rx.Observable.of(null))
}
}
StorageService.shared = new StorageService()
When I build the project I can see deviceuuid is 14c629ee-0dc7-43ef-91c2-eed642271670
Now I want to get the deviceuuid from another screen.
componentWillMount() {
console.log('LoginScreen componentWillMount');
console.log('rxInit data =>', AppService.shared.rxInit())
let testId = StorageService.shared.get('deviceuuid')
console.log('testID', testId);
StorageService.shared.get('deviceuuid').flatMap((deviceuuid) => {
console.log('deviceuuid', deviceuuid);
this.setState({ uuid: deviceuuid })
}).do((deviceuuid) => {
console.log(`LoginScreen deviceuuid is ${deviceuuid}`)
})
}
the result like the image
I have no idea how to get deviceuuid.
and the code is not working. I can't see any console log result.
StorageService.shared.get('deviceuuid').flatMap((deviceuuid) => {
console.log('deviceuuid', deviceuuid);
this.setState({ uuid: deviceuuid })
}).do((deviceuuid) => {
console.log(`LoginScreen deviceuuid is ${deviceuuid}`)
})
I'm not familiar with rxjs, any help would be appreciated.
I'm trying to call a Vuex getter in the component watch.
But it tells me that it is undefined what is not.
So i try to return the getter in the computed object but still doesn t work.
name: "FreeTalk",
computed: {
...mapGetters(['getCharacter','getResultStatus', 'getFreeTalkText', 'getResult', 'freeTalkResult', 'getFreeTalkNoNative', 'getFreeTalkMedium','getFreeTalkNative']),
progressStatus() {
return this.getResult.progress
},
getStatus() {
return this.getResultStatus
}
},
watch: {
progressStatus: (val) => {
if (val == 100) {
this.status = this.getStatus()
if (this.getStatus === 0) {
this.outputText = this.getFreeTalkNoNative.result;
} else if (this.getStatus === 1) {
this.outputText = this.getFreeTalkMedium;
} else if (this.getStatus === 2) {
this.outputText = this.getFreeTalkNative;
}
}
}
},
data() {
return {
outputText: '',
ready: false,
isRecordDone: false,
status: -1
}
}
}
I'm new at mithril.js. I have a div, I want to add class "invalid" if ctrl.invalid()==true, and "hidden" if ctrl.hidden()==true.
If I use m('div', {class: ctrl.invalid() ? 'invalid' : '', class: ctrl.hidden()? 'hidden' : ''}), they override each other.
I can use m('div', {class: [ctrl.invalid()?'invalid':'', ctrl.focused()?'focused':''].join(' ')}), and it'll work, but it looks messy.
Is there an elegant solution for this? Thanks.
I recommend you to use classnames - a simple utility for that. You can define your classes in a nice way and it will merge everything for you. In your case it will be:
const myMergedClasses = classNames({
invalid: ctrl.invalid(),
focused: ctrl.focused()
});
m('div', { class: myMergedClasses })
Beautiful?!
Very late to the game, but as an inspiration for others ending up here, I often do something like the following, just because it is:
simple to implement
easy to extend
easy to understand
view(): {
const classes =
`${ctrl.invalid() ? '.invalid' : ''}` +
`${ctrl.hidden()? '.hidden' : ''}`;
return m(`div${classes}`);
}
You can add a helper method to your Mithril component:
const myComponent = {
css() {
// Add some logic
return 'class1 class2';
},
view() {
return m('div', { class: this.css() });
},
};
Or to the controller:
const ctrl = {
css() {
// Add some logic
return 'class3';
},
};
const myComponent = {
view() {
return m('div', { class: ctrl.css() });
},
};
Choose whichever suits your case better.
You can also use the classnames utility, as suggested by Ross Khanas in his answer:
const myComponent = {
css() {
return classNames({
invalid: ctrl.invalid(),
focused: ctrl.focused(),
});
},
view() {
return m('div', { class: this.css() });
},
};
Or:
const ctrl = {
css() {
return classNames({
invalid: this.invalid(),
focused: this.focused(),
});
},
invalid() { /* ... */ },
focused() { /* ... */ },
};
const myComponent = {
view() {
return m('div', { class: ctrl.css() });
},
};