Git Product home page Git Product logo

vocdoni / vocdoni-mobile Goto Github PK

View Code? Open in Web Editor NEW
12.0 5.0 3.0 3.32 MB

Decentralized, transparent, verifiable and anonymous voting app

License: BSD 3-Clause "New" or "Revised" License

Kotlin 0.07% Ruby 0.51% Swift 0.10% Objective-C 0.01% Dart 96.21% HTML 0.73% Makefile 1.30% JavaScript 1.07%
voting voting-application voting-app flutter decentralized decentralized-voting vote vote-application evoting governance

vocdoni-mobile's Introduction

Vocdoni Mobile Client

Official implementation of the Vocdoni core features.

Flavors

The app can run in three diferent modes:

  • Development
    • Uses the development blockchain
  • Beta (Android only)
    • Uses the development blockchain
  • Production
    • Uses the production blockchain

Flavors are available on Android. The iOS project uses dev and production depending on the XCode target.

Development

Data Architecture and internal Models

The State Management architecture of the app is built on top of the Eventual package. Eventual allows to track updates on objects and rebuild their corresponding UI accordingly.

Model classes

They can be of the type:

  • Single models
    • AppStateModel, AccountModel, EntityModel, ProcessModel, FeedModel
    • A model can contain references to other models
  • Pools of data
    • Typically, they contain a collection of single models
    • Usually used as global variables
    • Their goal is to track whether the collection changes, but not the individual items
    • They contain all the model instances known to the system
    • Any modification on a model should happen in models obtained from a global pool, since the pool manages persistence

This separation allows for efficient and granular widget tree rebuilds whenever the state is updated. If a single value changes, only the relevant children should rebuild.

Usage

Initialize and read Global Model's data in globals.dart

// entitiesPersistence = EntitiesPersistence();
await Global.entitiesPersistence.readAll();

// ...
// entityPool = EntityPoolModel();
await Globals.entityPool.readFromStorage();  // will import and arrange the persisted data

Consume Models in specific places

Typically, a Pool with all the EntityModel's known to the app and then, individual EntityModel instances when the user selects one.

// Globals.entityPool

// ...

// Widget
@override
Widget build(BuildContext context) {
	// From the pool, we grab the first entity model
	final myEntity = Globals.entityPool.value.first;

	// Consume many values (EventualNotifier) locally
	return EventualBuilder(
    	notifiers: [myEntity.feed, myEntity.processes],  // EventualNotifier<T> values that may change over time
		builder: (context) {
			// rebuilt whenever either of myEntity.feed or myEntity.processes change

			// ...
		)
	);
}

In the example above, updates on specifig Feed items, will not affect the current widget. But as soon as we call myEntity.feed.refresh() on this instance, the Builder will be triggered because of the changes in isLoading, hasError and hasValue.

Extra methods

Certain models implement the ModelRefreshable interface. This ensures that callers can call refresh() to request a refetch of remote data, based on the current model's ID or metadata.

Other models (mainly pools) also implement the ModelPersistable interface, so that readFromStorage() and writeToStorage() can be called.

General

It is important not to mix the models (account, entity, process, feed, app state) with the Persistence classes. Persistence classes map diretly to dvote-protobuf classes, which allow for binary serialization and consistently have a 1:1 mapping.

Models can contain both data which is persisted (entity metadata, process metadata) as well as data that is ephemeral (current participants on a vote, selected account). When readFromStorage is called, data needs to be deserialized and restored properly, often across multiple models.

Internationalization

Translations

  • Add import 'package:vocdoni/lib/i18n.dart'; on your widget file
  • Access the new string with getText(context, "My new string to translate")
  • Parse the new strings with make lang-extract
  • Translate the files on assets/i18n/*.json

The app's strings translation can be found on Weblate.

Translation management

Weblate monitors origin/i18n and pulls from it as new strings are available. After a new translation is added, Weblate compiles the new JSON files in a git remote of its own.

To pull from it, run make init. This will add a weblate git remote besides origin. This way:

  • weblate/i18n has the translated strings to integrate into the app
  • origin/i18n contains the empty strings that Weblate will show to translators

The translation flow is an iterative loop that looks like:

  • Lock the weblate repository
  • git checkout i18n
  • git pull weblate i18n (update our local repo)
  • git push origin i18n (update the GitHub branch)
  • git checkout main (check out the latest code)
  • git merge i18n (integrate the latest translations)
  • git push origin main (update the GitHub branch)
  • git checkout i18n
  • git merge main (integrate the latest code into i18n)
  • make lang-parse (extract the new strings)
  • git add assets/i18n/* (stage the language files for commit)
  • git commit -m "Updated strings"
  • git push origin i18n (push the new strings for Weblate)
  • git checkout main
  • Unlock the Weblate repository

Important:

  • Make sure to use the right key prefixes:
    • "action.createIdentity" instead of "main.createIdentity"
  • In the keys, use no symbols beyond the dot separator
  • Use placeholders for data replacement instead of concatenating it later
    • "question.doYouWantToRemoveName" => "Do you want to remove {{NAME}}?"

Dependencies

The project makes use of the DVote Flutter plugin. Please, see the repository for more details.

Integration

Deep linking

The app accepts Deep Links from the following domains:

  • app.vocdoni.net
  • app.dev.vocdoni.net
  • vocdoni.page.link
  • vocdonidev.page.link

To enable them:

  • Place linking/assetlink.json on https://app.vocdoni.net/.well-known/assetlinks.json
    • Also place linking/assetlink.json on https://app.dev.vocdoni.net/.well-known/assetlinks.json
  • Place linking/apple-app-site-association on https://app.vocdoni.net/.well-known/apple-app-site-association

Supported Paths and Parameters

  • app.vocdoni.net and app.dev.vocdoni.net
    • https://app.vocdoni.net/entities/#/<entity-id>
    • https://app.vocdoni.net/entities/#/<process-id>
    • https://app.vocdoni.net/news/#/<process-id>
    • https://app.vocdoni.net/validation/#/<entity-id>/<validation-token>

The same applies to app.dev.vocdoni.net

  • vocdoni.page.link and vocdonidev.page.link
    • They wrap dynamic links
    • The link query string parameter is extracted, which should contain a link like the ones above from app.vocdoni.net and app.dev.vocdoni.net

Supported Schemas

The app also accepts URI's using the vocdoni: schema, with the same paths and parameters as above:

  • vocdoni://vocdoni.app/entities/#/<entity-id>
  • vocdoni://vocdoni.app/processes/#/<entity-id>/<process-id>
  • vocdoni://vocdoni.app/posts/#/<entity-id>/<idx>
  • vocdoni://vocdoni.app/validation/#/<entity-id>/<validation-token>

Show an entity

On developoment, you can test it by running make launch-ios-org or make launch-android-org

Push notifications

The data field of incoming push notifications is expected to contain three keys:

{
   notification: { ... },
   data: {
      uri: "https://vocdoni.link/processes/0x1234.../0x2345...",
      event: "new-process", // enum, see below
      message: "..." // same as notification > body
   }
}
  • The uri field will be used the same as a deep link and will determine where the app navigates to.
  • The event can be one of:
    • entity-updated: The metadata of the entity has changed (but not the process list or the news feed)
    • new-post: A new post has been added to the Feed
    • new-process: A process has been created
    • process-ended: The end block has been reached
  • The message is a copy of the relevant text contained within notification (not always present)

Permissions

In order to drop unused permissions, edit ios/Podfile and make sure to uncomment the permissions that are not needed after target.build_configurations.each.

Troubleshooting

  • The r_scan plugin breaks the build on iOS and/or is rejected by Google Play
    • Edit pubspec.yaml and comment/uncomment the dependencies accordingly
  • Can't compile iOS because App.framework is built for another architecture
    • Run rm -Rf ios/Flutter/App.framework and try again

vocdoni-mobile's People

Contributors

brickpop avatar comradekingu avatar jordipainan avatar kelemeno avatar luizfernandosg avatar natewilliams2 avatar nathanbnm avatar xavivives avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

vocdoni-mobile's Issues

Add a screen to ask for the recovery questions

  • Adapt the recovery screen to support copying links instead of just entering a mnemonic
  • Add a new screen to request the question responses
  • Validate the link and create a new account if successful

[app] Launch the email compose screen

  • Add a Flutter plugin to compose emails using the OS dialog
  • Add text along with the recovery link so it can be searched for in the future, and instructions to get back to the app
  • Embed the version number on the URL

The activation link doesn't work properly (app doesn't oppen)

Describe the bug
My android device doesn't detect my activation link (sent using vocdoni-manager) as a link to be opened with Vocdoni app, even though it's installed in my device.

To Reproduce
Steps to reproduce the behavior:

  1. Go to vocdoni-manager and send an activation link
  2. Receive the link and open it with an android device using xiaomi 12.0.4 + Firefox 85.1.1
  3. See the bug

Current behavior
When you open the activation link in the device, it just opens the browser to redirect you to the Vocdoni page in the Google playstore instead of triggering Vocdoni app.

Expected behavior
The link should trigger Vocdoni app to complete the user activation.

Infrastructure (please complete the following information):

  • OS & version: Android 10 with MIUI 12.0.4
  • Browser: Firefox 85.1.1
  • Vocdoni app version: 0.8.16

Additional context
This is a critical bug to solve for the open access. Not sure if it's a dependency of the app, firebase or any other infrastructure.

bug: Some members of an entity are receiving wrong metadata in their app

Describe the bug
Three members of this entity are receiving information (voting processes) that doesn't correspond to the entity they are following (see the image attached in the additional context section). The rest of members can see the information correctly and were able to vote in the last voting process (now ended).

Update: For some reason, two of these three members opened the application again yesterday, saw the voting process correctly and were able to vote.

System (please complete the following information):

  • Software version: 0.8.16 i 0.8.15+42

Additional context
7d55405e-2103-4bf0-955d-b09d0d6f7239

Render entities' descriptions as HTML

In the manager we're adding support for entities to be able to tweak their description with a WYSIWYG editor.

Due to this the app will need to start parsing entities descriptions as HTML wherever needed.

The contents are (or should be) stored already sanitized, so there's no need to sanitize tags on your side.

bug: backup link doesn't work when the username has 2 or more words

Describe the bug
When you create an identity with a name composed of 2 or more words and send a backup link, it appears broken in the email that is sent. Probably because %20 is missing

To Reproduce (please complete the following information)

  • Steps to reproduce the behavior:
    1. Open Vocdoni app (Beta)
    2. Crete a new identity with a username composed by 2 or more words (for example John Doe)
    3. Send the backup link to your email
    4. See the error

Additional context
See attached image

There is a limit of 35 characters in the UI for the Options

When you visualize a vote in the app, the option' texts are limited to 35 characters by the UI.
So there is no way to read a long text within the app.

Could we find a solution for that?
Setting a limit of 35 characters would not be a good option. Since certain processes require long options.
Perhaps a text that moves from left to right? Or enable several lines in the UI for long texts.

@xavivives I would like to receive your inputs on this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.