Beispiel #1
0
func (m *PriceStore) Handle(payload *flux.Payload) (finished bool) {
	switch payload.Action.(type) {
	case *UpdateCountryAction, *UpdateCity:
		payload.Wait(m.app.City)
		m.price = calculatePrice(m.app.Country.GetCountry(), m.app.City.GetCity())
	}
	return true
}
Beispiel #2
0
func (m *MessageStore) Handle(payload *flux.Payload) (finished bool) {
	switch action := payload.Action.(type) {
	case *AddTopic:
		payload.Wait(m.app.Topics)
		m.messages = append(m.messages, action.Message)
	case *AddMessage:
		m.messages = append(m.messages, action.Message)
	}
	return true
}
Beispiel #3
0
func (m *CityStore) Handle(payload *flux.Payload) (finished bool) {
	switch action := payload.Action.(type) {
	case *UpdateCity:
		m.city = action.City
	case *UpdateCountryAction:
		payload.Wait(m.app.Country)
		m.city = getCapital(m.app.Country.GetCountry())
	}
	return true
}
Beispiel #4
0
func (s *PackageStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.InitialState:
		pkgNode, err := node.Unmarshal(s.ctx, action.Info.Package)
		if err != nil {
			s.app.Fail <- kerr.Wrap("KXIKEWOKJI", err)
		}
		s.node = pkgNode
		s.filename = action.Info.PackageFilename
		payload.Notify(nil, PackageChanged)
	}
	return true
}
Beispiel #5
0
Datei: rule.go Projekt: kego/ke
func (s *RuleStore) validateNodes(payload *flux.Payload, changes []*node.Node) {
	for _, n := range changes {
		m := s.app.Nodes.Get(n)
		changed, err := m.Validate(s.ctx, s.app.Rule.Get(n.Root(), n))
		if err != nil {
			s.app.Fail <- kerr.Wrap("BYQOBLPRDP", err)
			break
		}
		if changed {
			payload.Notify(n, NodeErrorsChanged)
		}
	}
}
Beispiel #6
0
func (s *st1) Handle(payload *flux.Payload) (finished bool) {
	async := payload.Action.(bool)
	if async {
		go func() {
			<-handleDone
			payload.Notify("b", notif1{})
			close(notifyDone)
		}()
	} else {
		payload.Notify("a", notif1{})
	}
	return true
}
Beispiel #7
0
Datei: type.go Projekt: kego/ke
func (s *TypeStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.InitialState:
		types := action.Info.Imports[action.Info.Path].Types
		if len(types) == 0 {
			break
		}
		for name, ti := range types {
			typ, err := node.Unmarshal(s.ctx, ti.Bytes)
			if err != nil {
				s.app.Fail <- kerr.Wrap("QDOVEIKABS", err)
			}
			s.types[name] = &models.TypeModel{
				Node: typ,
				File: ti.File,
			}

		}
		payload.Notify(nil, TypeChanged)
	}
	return true
}
Beispiel #8
0
Datei: misc.go Projekt: kego/ke
func (s *MiscStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.ToggleInfoState:
		s.info = !s.info
		payload.Notify(nil, InfoStateChange)
	case *actions.OpenAddPopup:
		s.addPopup = &models.AddPopupModel{
			Visible: true,
			Parent:  action.Parent,
			Node:    action.Node,
			Types:   action.Types,
		}
		payload.Notify(nil, AddPopupChange)
	case *actions.CloseAddPopup:
		s.addPopup = &models.AddPopupModel{
			Visible: false,
		}
		payload.Notify(nil, AddPopupChange)
	}
	return true
}
Beispiel #9
0
Datei: editor.go Projekt: kego/ke
func (s *EditorStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.EditorData:
		action.Func(payload)

	case *actions.Add:
		payload.Wait(s.app.Branches)
		switch action.Direction() {
		case actions.New, actions.Redo:
			mutateAddEditor(s, action.Node, action.Parent)
		case actions.Undo:
			mutateDeleteEditor(s, action.Node, action.Parent)
		}

	case *actions.Delete:
		payload.Wait(s.app.Branches)
		switch action.Direction() {
		case actions.New, actions.Redo:
			mutateDeleteEditor(s, action.Node, action.Parent)
		case actions.Undo:
			mutateAddEditor(s, action.Node, action.Parent)
		}

	case *actions.InitialState:
		payload.Wait(s.app.Package, s.app.Types, s.app.Data)
		s.AddEditorsRecursively(s.app.Package.Node())
		for _, ti := range s.app.Types.All() {
			s.AddEditorsRecursively(ti.Node)
		}
	case *actions.LoadFileSuccess:
		ni, ok := action.Branch.Contents.(models.NodeContentsInterface)
		if !ok {
			break
		}
		n := ni.GetNode()
		s.AddEditorsRecursively(n)
	}
	return true
}
Beispiel #10
0
Datei: node.go Projekt: kego/ke
func (s *NodeStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.ToggleSystemControls:
		m := s.Get(action.Node)
		m.ShowSystemControls = !m.ShowSystemControls
		payload.Notify(action.Node, NodeSystemControls)
	case *actions.Add:
		payload.Wait(s.app.Actions)
		switch action.Direction() {
		case actions.New:
			if err := mutateAddNode(s.ctx, action.Node, action.Parent, action.Key, action.Index, action.Type, action.BranchName); err != nil {
				s.app.Fail <- kerr.Wrap("HUOGBUQCAO", err)
				break
			}
			payload.Notify(action.Node, NodeInitialised)
			if action.Parent != nil {
				payload.Notify(action.Parent, NodeChildAdded)
			}
			payload.Notify(action.Node, NodeFocus)
		case actions.Undo:
			action.Backup = node.NewNode()
			if err := mutateDeleteNode(s.ctx, action.Node, action.Parent, action.Backup); err != nil {
				s.app.Fail <- kerr.Wrap("RTAGMUIKMD", err)
				break
			}
			payload.Notify(action.Node, NodeDeleted)
			if action.Parent != nil {
				payload.Notify(action.Parent, NodeChildDeleted)
			}
		case actions.Redo:
			if err := mutateRestoreNode(s.ctx, action.Node, action.Parent, action.Backup); err != nil {
				s.app.Fail <- kerr.Wrap("MHUTMXOGBP", err)
				break
			}
			payload.Notify(action.Node, NodeInitialised)
			payload.Notify(action.Parent, NodeChildAdded)
			payload.Notify(action.Node, NodeFocus)
		}

		if action.Parent != nil {
			model := s.app.Nodes.Get(action.Parent)
			changed, err := model.Validate(s.ctx, s.app.Rule.Get(action.Parent.Root(), action.Parent))
			if err != nil {
				s.app.Fail <- kerr.Wrap("AKIUPRWGLV", err)
				break
			}
			if changed {
				payload.Notify(action.Parent, NodeErrorsChanged)
			}
		}

		c := action.Parent
		for c != nil {
			payload.Notify(c, NodeDescendantChanged)
			c = c.Parent
		}
	case *actions.Delete:
		payload.Wait(s.app.Actions)
		switch action.Direction() {
		case actions.New, actions.Redo:
			action.Backup = node.NewNode()
			if err := mutateDeleteNode(s.ctx, action.Node, action.Parent, action.Backup); err != nil {
				s.app.Fail <- kerr.Wrap("DFHTKJRLQC", err)
				break
			}
			payload.Notify(action.Node, NodeDeleted)
			payload.Notify(action.Parent, NodeChildDeleted)
		case actions.Undo:
			if err := mutateRestoreNode(s.ctx, action.Node, action.Parent, action.Backup); err != nil {
				s.app.Fail <- kerr.Wrap("HAPWUOPBTW", err)
				break
			}
			payload.Notify(action.Node, NodeInitialised)
			payload.Notify(action.Parent, NodeChildAdded)
			payload.Notify(action.Node, NodeFocus)
		}

		if action.Parent != nil {
			model := s.app.Nodes.Get(action.Parent)
			changed, err := model.Validate(s.ctx, s.app.Rule.Get(action.Parent.Root(), action.Parent))
			if err != nil {
				s.app.Fail <- kerr.Wrap("IOFCSITVXB", err)
				break
			}
			if changed {
				payload.Notify(action.Parent, NodeErrorsChanged)
			}
		}

		c := action.Parent
		for c != nil {
			payload.Notify(c, NodeDescendantChanged)
			c = c.Parent
		}
	case *actions.Reorder:
		payload.Wait(s.app.Actions)
		if !action.Model.Node.Type.IsNativeArray() {
			s.app.Fail <- kerr.New("EPBQVIICFM", "Must be array")
			break
		}
		from := action.Before
		to := action.After
		if action.Direction() == actions.Undo {
			from = action.After
			to = action.Before
		}
		if err := action.Model.Node.ReorderArrayChild(from, to); err != nil {
			s.app.Fail <- kerr.Wrap("HMIFPKVJCN", err)
			break
		}
		payload.Notify(action.Model.Node, NodeArrayReorder)
		c := action.Model.Node.Parent
		for c != nil {
			payload.Notify(c, NodeDescendantChanged)
			c = c.Parent
		}

	case *actions.Modify:
		payload.Wait(s.app.Actions)
		n := action.Editor.Node
		val := action.After
		if action.Direction() == actions.Undo {
			val = action.Before
		}
		if err := n.SetValue(s.ctx, val); err != nil {
			s.app.Fail <- kerr.Wrap("VIMXVIHPFY", err)
			break
		}
		model := s.app.Nodes.Get(n)
		changed, err := model.Validate(s.ctx, s.app.Rule.Get(n.Root(), n))
		if err != nil {
			s.app.Fail <- kerr.Wrap("EEIYMGQCCA", err)
			break
		}
		if changed {
			payload.Notify(n, NodeErrorsChanged)
		}

		payload.Notify(n, NodeValueChanged)

		c := n.Parent
		for c != nil {
			payload.Notify(c, NodeDescendantChanged)
			c = c.Parent
		}
	case *actions.EditorFocus:
		payload.Notify(action.Editor.Node, NodeFocus)
	case *actions.InitialState:
		payload.Wait(s.app.Package, s.app.Types)
		n := s.app.Package.Node()
		if err := n.RecomputeHash(s.ctx, true); err != nil {
			s.app.Fail <- kerr.Wrap("NDMDVGISWR", err)
		}
		for _, ti := range s.app.Types.All() {
			if err := ti.Node.RecomputeHash(s.ctx, true); err != nil {
				s.app.Fail <- kerr.Wrap("YLRDBXIYJH", err)
			}
		}
	case *actions.LoadFileSuccess:
		nci, ok := action.Branch.Contents.(models.NodeContentsInterface)
		if !ok {
			break
		}
		if err := nci.GetNode().RecomputeHash(s.ctx, true); err != nil {
			s.app.Fail <- kerr.Wrap("BWUPWAFALG", err)
		}
	}

	if m, ok := payload.Action.(actions.Mutator); ok {
		n := m.CommonAncestor()
		c := n
		for c != nil {
			// For the actual node, we recompute the whole hash. For ancestors,
			// we recompute the hash without recomputing child nodes.
			recomputeChildren := c == n
			if err := c.RecomputeHash(s.ctx, recomputeChildren); err != nil {
				s.app.Fail <- kerr.Wrap("SWHIXHLHXM", err)
			}
			c = c.Parent
		}
	}

	return true
}
Beispiel #11
0
Datei: branch.go Projekt: kego/ke
func (s *BranchStore) Handle(payload *flux.Payload) bool {
	previous := s.selected
	switch action := payload.Action.(type) {
	case *actions.ChangeView:
		s.view = action.View
		s.selected = s.Root()
		payload.Notify(nil, ViewChanged)
	case *actions.Add:
		payload.Wait(s.app.Nodes)
		switch action.Direction() {
		case actions.New, actions.Redo:
			child, parent, err := mutateAppendBranch(s, action.Parent, action.Node, action.BranchName, action.BranchFile)
			if err != nil {
				s.app.Fail <- kerr.Wrap("LDBMBRHWHB", err)
				break
			}
			if child != nil {
				if ancestor := child.EnsureVisible(); ancestor != nil {
					payload.NotifyWithData(ancestor, BranchOpened, &BranchDescendantSelectData{
						Branch: child,
						Op:     models.BranchOpChildAdded,
					})
				}
				s.selected = child
				payload.Notify(previous, BranchUnselectControl)
				payload.Notify(s.selected, BranchSelectControl)
				payload.Notify(s.selected, BranchSelected)
			}
			if parent != nil {
				payload.Notify(parent, BranchChildAdded)
			}
		case actions.Undo:
			_, parent, err := mutateDeleteBranch(s, action.Node)
			if err != nil {
				s.app.Fail <- kerr.Wrap("NLFWVSNNTY", err)
				break
			}
			if parent != nil {
				payload.Notify(parent, BranchChildDeleted)
				s.selected = parent
				payload.Notify(previous, BranchUnselectControl)
				payload.Notify(s.selected, BranchSelectControl)
				payload.Notify(s.selected, BranchSelected)
			}
		}
	case *actions.Delete:
		payload.Wait(s.app.Nodes)
		switch action.Direction() {
		case actions.New, actions.Redo:
			branch, parent, err := mutateDeleteBranch(s, action.Node)
			if err != nil {
				s.app.Fail <- kerr.Wrap("QTXPXAKXHH", err)
				break
			}
			if branch != nil {
				action.BranchIndex = branch.Index
				if nci, ok := branch.Contents.(models.NodeContentsInterface); ok {
					action.BranchName = nci.GetName()
				}
				if fci, ok := branch.Contents.(models.FileContentsInterface); ok {
					action.BranchFile = fci.GetFilename()
				}
			}
			if parent != nil {
				payload.Notify(parent, BranchChildDeleted)
				s.selected = parent
				payload.Notify(previous, BranchUnselectControl)
				payload.Notify(s.selected, BranchSelectControl)
				payload.Notify(s.selected, BranchSelected)
			}
		case actions.Undo:
			child, parent, err := mutateInsertBranch(s, action.Parent, action.Node, action.BranchIndex, action.BranchName, action.BranchFile)
			if err != nil {
				s.app.Fail <- kerr.Wrap("OOGOEWKPIL", err)
				break
			}
			if child != nil {
				if ancestor := child.EnsureVisible(); ancestor != nil {
					payload.NotifyWithData(ancestor, BranchOpened, &BranchDescendantSelectData{
						Branch: child,
						Op:     models.BranchOpChildAdded,
					})
				}
				s.selected = child
				payload.Notify(previous, BranchUnselectControl)
				payload.Notify(s.selected, BranchSelectControl)
				payload.Notify(s.selected, BranchSelected)
			}
			if parent != nil {
				payload.Notify(parent, BranchChildAdded)
			}
		}
	case *actions.Reorder:
		payload.Wait(s.app.Nodes)
		parent, err := mutateReorderBranch(s, action.Model.Node)
		if err != nil {
			s.app.Fail <- kerr.Wrap("NUQOPWWXHA", err)
			break
		}
		if parent != nil {
			payload.Notify(parent, BranchChildrenReordered)
		}
	case *actions.BranchClose:
		if !action.Branch.CanOpen() {
			// branch can't open - ignore
			break
		}
		action.Branch.RecursiveClose()
		payload.Notify(action.Branch, BranchClose)
	case *actions.BranchOpening:
		if !action.Branch.CanOpen() {
			// branch can't open - ignore
			break
		}
		// The branch may not be loaded, so we don't open the branch until the BranchOpenPostLoad
		// action is received. This will happen immediately if the branch is loaded or not async.
		payload.Notify(action.Branch, BranchOpening)
	case *actions.BranchOpened:
		if !action.Branch.CanOpen() {
			// branch can't open - ignore
			break
		}
		action.Branch.Open = true
		payload.Notify(action.Branch, BranchOpened)

	case *actions.BranchSelecting:

		if ancestor := action.Branch.EnsureVisible(); ancestor != nil {
			payload.NotifyWithData(ancestor, BranchOpened, &BranchDescendantSelectData{
				Branch: action.Branch,
				Op:     action.Op,
			})
			break
		}

		s.selected = action.Branch

		payload.Notify(previous, BranchUnselectControl)
		payload.Notify(s.selected, BranchSelectControl)

		if action.Op == models.BranchOpKeyboard {
			go func() {
				<-time.After(common.EditorKeyboardDebounceShort)
				if s.selected == action.Branch {
					payload.NotifyWithData(
						s.selected,
						BranchSelecting,
						&BranchSelectOperationData{Op: action.Op},
					)
				}
			}()
		} else {
			payload.NotifyWithData(
				s.selected,
				BranchSelecting,
				&BranchSelectOperationData{Op: action.Op},
			)
		}

	case *actions.BranchSelected:
		payload.Wait(s.app.Nodes)
		payload.Notify(s.selected, BranchSelected)
	case *actions.InitialState:
		payload.Wait(s.app.Package, s.app.Types, s.app.Data, s.app.Env)

		s.pkg = s.NewFileBranchModel(s.ctx, s.app.Package.Node(), "package", s.app.Package.Filename())
		s.pkg.Root = true
		s.pkg.Open = true

		s.types = models.NewBranchModel(s.ctx, &models.TypesContents{})
		s.types.Root = true
		s.types.Open = true
		for _, name := range s.app.Types.Names() {
			t := s.app.Types.Get(name)
			typeBranch := s.NewFileBranchModel(s.ctx, t.Node, name, t.File)
			s.types.Append(typeBranch)
		}

		s.data = models.NewBranchModel(s.ctx, &models.DataContents{})
		s.data.Root = true
		s.data.Open = true

		for _, name := range s.app.Data.Names() {
			s.data.Append(models.NewBranchModel(s.ctx, &models.AsyncContents{
				FileContents: models.FileContents{
					NodeContents: models.NodeContents{
						Name: name,
					},
					Filename: s.app.Data.Get(name).File,
				},
			}))
		}
		s.selected = s.data
		payload.Notify(nil, BranchInitialStateLoaded)
	case *actions.LoadFileSuccess:
		ni, ok := action.Branch.Contents.(models.NodeContentsInterface)
		if !ok {
			break
		}
		n := ni.GetNode()
		s.AppendNodeBranchModelChildren(action.Branch, n)
		s.nodeBranches[n] = action.Branch
		payload.Notify(action.Branch, BranchLoaded)
	}
	return true
}
Beispiel #12
0
Datei: action.go Projekt: kego/ke
func (s *ActionStore) Handle(payload *flux.Payload) bool {

	u, ok := payload.Action.(actions.Undoable)
	if !ok {
		return true
	}
	switch u.Direction() {
	case actions.New:
		s.actions = append(s.actions[:s.index], u)
		s.index = len(s.actions)
	case actions.Undo:
		if s.index <= 0 {
			break
		}
		s.index--
	case actions.Redo:
		if s.index >= len(s.actions) {
			break
		}
		s.index++
	}
	payload.Notify(nil, ActionsChanged)

	/*
		switch action := payload.Action.(type) {
		case *actions.Add:
			action.Action = &Add{
				Node:   action.Node,
				Parent: action.Parent,
				Key:    action.Key,
				Index:  len(action.Parent.Array),
			}
			s.Add(action.Action)
		case *actions.Delete:
			action.Action = &Delete{
				Parent: action.Node.Parent,
				Node:   action.Node,
			}
			s.Add(action.Action)
		case *actions.Modify:
			action.Action = &Modify{
				Node:   action.Editor.Node,
				Before: action.Editor.Node.Value,
				After:  action.Value,
			}
			s.Add(action.Action)
		case *actions.Reorder:
			if !action.Model.Node.Type.IsNativeArray() {
				s.app.Fail <- kerr.New("EPBQVIICFM", "Must be array")
				break
			}
			action.Action = &Reorder{
				Parent: action.Model.Node,
				Before: action.OldIndex,
				After:  action.NewIndex,
			}
			s.Add(action.Action)
		case *actions.Undo:
			if s.index == 0 {
				break
			}
			s.index--
			action.Action = s.actions[s.index]
		case *actions.Redo:
			if s.index == len(s.actions) {
				break
			}
			s.index++
			action.Action = s.actions[s.index]
		}*/
	return true
}
Beispiel #13
0
Datei: rule.go Projekt: kego/ke
func (s *RuleStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.InitialState:
		payload.Wait(s.app.Package, s.app.Types)
		s.validateNodes(payload, s.build(s.app.Package.Node()))
		for _, t := range s.app.Types.All() {
			s.validateNodes(payload, s.build(t.Node))
		}
	case *actions.LoadFileSuccess:
		payload.Wait(s.app.Branches)
		ni, ok := action.Branch.Contents.(models.NodeContentsInterface)
		if !ok {
			break
		}
		n := ni.GetNode()
		s.validateNodes(payload, s.build(n))
	case *actions.Modify:
		payload.Wait(s.app.Nodes)
		// Node mutations that are in response to keyboard input should wait an
		// additional time before doing a full validation rebuild. This
		// operation may be expensive, so we shouldn't risk interupting the
		// user input.
		if action.Direction() != actions.New || action.Immediate {
			// Undo and Redo actions are from mouse input, so always immediate.
			// New actions are immediate if action.Immediate == true
			s.validateNodes(payload, s.build(action.Editor.Node.Root()))
			break
		}
		// Delayed actions wait an extra time before the validation rebuild.
		go func() {
			// We have already waited for EditorKeyboardDebounceShort, so we
			// reduce the duration by this.
			<-time.After(common.EditorKeyboardDebounceLong - common.EditorKeyboardDebounceShort)
			if action.Changed() {
				return
			}
			s.validateNodes(payload, s.build(action.Editor.Node.Root()))
		}()
	case *actions.Reorder:
		payload.Wait(s.app.Nodes)
		s.validateNodes(payload, s.build(action.Model.Node.Root()))
	case *actions.Delete:
		payload.Wait(s.app.Nodes)
		s.validateNodes(payload, s.build(action.Node.Root()))
	case *actions.Add:
		payload.Wait(s.app.Nodes)
		s.validateNodes(payload, s.build(action.Node.Root()))
	}
	return true
}
Beispiel #14
0
Datei: file.go Projekt: kego/ke
func (s *FileStore) Handle(payload *flux.Payload) bool {
	switch action := payload.Action.(type) {
	case *actions.Add:
		payload.Wait(s.app.Nodes)
		if action.Parent != nil {
			// only root nodes are files
			break
		}
		switch action.Direction() {
		case actions.New, actions.Redo:
			n := action.Node
			n.RecomputeHash(s.ctx, true)
			fm := &models.FileModel{
				Package:  false,
				Type:     *action.Type.Id == *system.NewReference("kego.io/system", "type"),
				Filename: action.BranchFile,
				Node:     n,
				Hash:     n.Hash(),
			}
			s.files[n] = fm
		case actions.Undo:
			delete(s.files, action.Node)
		}
		payload.Notify(nil, FileChangedStateChange)
	case *actions.SaveFileSuccess:
		for _, file := range action.Response.Files {
			f := s.fileByName(file.File)
			if f == nil {
				s.app.Fail <- kerr.New("FPXNHFJNOR", "File %s not found", file.File)
			}
			f.SaveHash = file.Hash
		}
		payload.Notify(nil, FileChangedStateChange)
	case *actions.InitialState:
		payload.Wait(s.app.Nodes)
		n := s.app.Package.Node()
		fm := &models.FileModel{
			Package:  true,
			Type:     false,
			Filename: s.app.Package.Filename(),
			Node:     n,
			Hash:     n.Hash(),
			LoadHash: n.Hash(),
		}
		s.files[n] = fm

		for _, ti := range s.app.Types.All() {
			n := ti.Node
			fm := &models.FileModel{
				Package:  false,
				Type:     true,
				Filename: ti.File,
				Node:     n,
				Hash:     n.Hash(),
				LoadHash: n.Hash(),
			}
			s.files[n] = fm
		}
	case *actions.LoadFileSuccess:
		sci, ok := action.Branch.Contents.(models.FileContentsInterface)
		if !ok {
			break
		}
		n := sci.GetNode()
		fm := &models.FileModel{
			Package:  false,
			Type:     false,
			Filename: sci.GetFilename(),
			Node:     n,
			Hash:     n.Hash(),
			LoadHash: n.Hash(),
		}
		s.files[n] = fm
	}

	if m, ok := payload.Action.(actions.Mutator); ok {
		payload.Wait(s.app.Nodes)
		n := m.CommonAncestor().Root()
		f, ok := s.files[n]
		if ok {
			prevChanged := f.Changed()
			f.Hash = n.Hash()
			newChanged := f.Changed()
			if prevChanged != newChanged {
				// state changed!
				payload.Notify(n, FileChangedStateChange)
			}
		}
	}
	return true
}